pgbelt 0.7.2__py3-none-any.whl → 0.7.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pgbelt/cmd/setup.py +2 -16
- pgbelt/cmd/status.py +1 -8
- pgbelt/cmd/sync.py +2 -16
- pgbelt/config/models.py +19 -9
- pgbelt/config/remote.py +1 -1
- pgbelt/util/pglogical.py +1 -1
- pgbelt/util/postgres.py +6 -18
- {pgbelt-0.7.2.dist-info → pgbelt-0.7.4.dist-info}/METADATA +2 -2
- {pgbelt-0.7.2.dist-info → pgbelt-0.7.4.dist-info}/RECORD +12 -12
- {pgbelt-0.7.2.dist-info → pgbelt-0.7.4.dist-info}/LICENSE +0 -0
- {pgbelt-0.7.2.dist-info → pgbelt-0.7.4.dist-info}/WHEEL +0 -0
- {pgbelt-0.7.2.dist-info → pgbelt-0.7.4.dist-info}/entry_points.txt +0 -0
pgbelt/cmd/setup.py
CHANGED
|
@@ -41,14 +41,7 @@ async def _setup_src_node(
|
|
|
41
41
|
|
|
42
42
|
pglogical_tables = pkey_tables
|
|
43
43
|
if conf.tables:
|
|
44
|
-
pglogical_tables = [
|
|
45
|
-
t
|
|
46
|
-
for t in pkey_tables
|
|
47
|
-
if t
|
|
48
|
-
in list(
|
|
49
|
-
map(str.lower, conf.tables)
|
|
50
|
-
) # Postgres returns table names in lowercase (in analyze_table_pkeys)
|
|
51
|
-
]
|
|
44
|
+
pglogical_tables = [t for t in pkey_tables if t in conf.tables]
|
|
52
45
|
|
|
53
46
|
# Intentionally throw an error if no tables are found, so that the user can correct their config.
|
|
54
47
|
# When reported by a certain user, errors showed when running the status command, but it was ignored,
|
|
@@ -161,14 +154,7 @@ async def setup_back_replication(config_future: Awaitable[DbupgradeConfig]) -> N
|
|
|
161
154
|
|
|
162
155
|
pglogical_tables = pkeys
|
|
163
156
|
if conf.tables:
|
|
164
|
-
pglogical_tables = [
|
|
165
|
-
t
|
|
166
|
-
for t in pkeys
|
|
167
|
-
if t
|
|
168
|
-
in list(
|
|
169
|
-
map(str.lower, conf.tables)
|
|
170
|
-
) # Postgres returns table names in lowercase (in analyze_table_pkeys)
|
|
171
|
-
]
|
|
157
|
+
pglogical_tables = [t for t in pkeys if t in conf.tables]
|
|
172
158
|
|
|
173
159
|
await configure_replication_set(
|
|
174
160
|
dst_root_pool, pglogical_tables, conf.schema_name, dst_logger
|
pgbelt/cmd/status.py
CHANGED
|
@@ -92,14 +92,7 @@ async def status(conf_future: Awaitable[DbupgradeConfig]) -> dict[str, str]:
|
|
|
92
92
|
all_tables = pkey_tables + non_pkey_tables
|
|
93
93
|
target_tables = all_tables
|
|
94
94
|
if conf.tables:
|
|
95
|
-
target_tables = [
|
|
96
|
-
t
|
|
97
|
-
for t in all_tables
|
|
98
|
-
if t
|
|
99
|
-
in list(
|
|
100
|
-
map(str.lower, conf.tables)
|
|
101
|
-
) # Postgres gave us lowercase table names in analyze_table_pkeys
|
|
102
|
-
]
|
|
95
|
+
target_tables = [t for t in all_tables if t in conf.tables]
|
|
103
96
|
|
|
104
97
|
if not target_tables:
|
|
105
98
|
raise ValueError(
|
pgbelt/cmd/sync.py
CHANGED
|
@@ -77,14 +77,7 @@ async def dump_tables(
|
|
|
77
77
|
_, tables, _ = await analyze_table_pkeys(src_pool, conf.schema_name, logger)
|
|
78
78
|
|
|
79
79
|
if conf.tables:
|
|
80
|
-
tables = [
|
|
81
|
-
t
|
|
82
|
-
for t in tables
|
|
83
|
-
if t
|
|
84
|
-
in list(
|
|
85
|
-
map(str.lower, conf.tables)
|
|
86
|
-
) # Postgres returns table names in lowercase (in analyze_table_pkeys)
|
|
87
|
-
]
|
|
80
|
+
tables = [t for t in tables if t in conf.tables]
|
|
88
81
|
|
|
89
82
|
await dump_source_tables(conf, tables, logger)
|
|
90
83
|
|
|
@@ -192,14 +185,7 @@ async def _dump_and_load_all_tables(
|
|
|
192
185
|
) -> None:
|
|
193
186
|
_, tables, _ = await analyze_table_pkeys(src_pool, conf.schema_name, src_logger)
|
|
194
187
|
if conf.tables:
|
|
195
|
-
tables = [
|
|
196
|
-
t
|
|
197
|
-
for t in tables
|
|
198
|
-
if t
|
|
199
|
-
in list(
|
|
200
|
-
map(str.lower, conf.tables)
|
|
201
|
-
) # Postgres returns table names in lowercase (in analyze_table_pkeys)
|
|
202
|
-
]
|
|
188
|
+
tables = [t for t in tables if t in conf.tables]
|
|
203
189
|
await dump_source_tables(conf, tables, src_logger)
|
|
204
190
|
await load_dumped_tables(conf, tables, dst_logger)
|
|
205
191
|
|
pgbelt/config/models.py
CHANGED
|
@@ -9,7 +9,8 @@ from pgbelt.util import get_logger
|
|
|
9
9
|
from pgbelt.util.asyncfuncs import makedirs
|
|
10
10
|
from pydantic import BaseModel
|
|
11
11
|
from pydantic import ValidationError
|
|
12
|
-
from pydantic import
|
|
12
|
+
from pydantic import field_validator
|
|
13
|
+
from urllib.parse import quote
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def config_dir(db: str, dc: str) -> str:
|
|
@@ -37,7 +38,7 @@ class User(BaseModel):
|
|
|
37
38
|
name: str
|
|
38
39
|
pw: Optional[str] = None
|
|
39
40
|
|
|
40
|
-
_not_empty =
|
|
41
|
+
_not_empty = field_validator("name", "pw")(not_empty)
|
|
41
42
|
|
|
42
43
|
|
|
43
44
|
class DbConfig(BaseModel):
|
|
@@ -64,9 +65,9 @@ class DbConfig(BaseModel):
|
|
|
64
65
|
pglogical_user: User
|
|
65
66
|
other_users: Optional[list[User]] = None
|
|
66
67
|
|
|
67
|
-
_not_empty =
|
|
68
|
+
_not_empty = field_validator("host", "ip", "db", "port")(not_empty)
|
|
68
69
|
|
|
69
|
-
@
|
|
70
|
+
@field_validator("root_user", "owner_user", "pglogical_user")
|
|
70
71
|
def has_password(cls, v) -> User: # noqa: N805
|
|
71
72
|
if not v.pw:
|
|
72
73
|
raise ValueError
|
|
@@ -86,15 +87,24 @@ class DbConfig(BaseModel):
|
|
|
86
87
|
|
|
87
88
|
@property
|
|
88
89
|
def root_uri(self) -> str:
|
|
89
|
-
|
|
90
|
+
password = quote(
|
|
91
|
+
self.root_user.pw
|
|
92
|
+
) # https://github.com/encode/databases/issues/145#issuecomment-1303792343 need this to handle special characters
|
|
93
|
+
return f"postgresql://{self.root_user.name}:{password}@{self.ip}:{self.port}/{self.db}"
|
|
90
94
|
|
|
91
95
|
@property
|
|
92
96
|
def owner_uri(self) -> str:
|
|
93
|
-
|
|
97
|
+
password = quote(
|
|
98
|
+
self.owner_user.pw
|
|
99
|
+
) # https://github.com/encode/databases/issues/145#issuecomment-1303792343 need this to handle special characters
|
|
100
|
+
return f"postgresql://{self.owner_user.name}:{password}@{self.ip}:{self.port}/{self.db}"
|
|
94
101
|
|
|
95
102
|
@property
|
|
96
103
|
def pglogical_uri(self) -> str:
|
|
97
|
-
|
|
104
|
+
password = quote(
|
|
105
|
+
self.pglogical_user.pw
|
|
106
|
+
) # https://github.com/encode/databases/issues/145#issuecomment-1303792343 need this to handle special characters
|
|
107
|
+
return f"postgresql://{self.pglogical_user.name}:{password}@{self.ip}:{self.port}/{self.db}"
|
|
98
108
|
|
|
99
109
|
|
|
100
110
|
class DbupgradeConfig(BaseModel):
|
|
@@ -118,7 +128,7 @@ class DbupgradeConfig(BaseModel):
|
|
|
118
128
|
sequences: Optional[list[str]] = None
|
|
119
129
|
schema_name: Optional[str] = "public"
|
|
120
130
|
|
|
121
|
-
_not_empty =
|
|
131
|
+
_not_empty = field_validator("db", "dc")(not_empty)
|
|
122
132
|
|
|
123
133
|
@property
|
|
124
134
|
def file(self) -> str:
|
|
@@ -167,7 +177,7 @@ class DbupgradeConfig(BaseModel):
|
|
|
167
177
|
return None
|
|
168
178
|
|
|
169
179
|
try:
|
|
170
|
-
out = cls.
|
|
180
|
+
out = cls.model_validate_json(raw)
|
|
171
181
|
except ValidationError:
|
|
172
182
|
logger.info("Cached config was not a valid DbupgradeConfig")
|
|
173
183
|
return None
|
pgbelt/config/remote.py
CHANGED
|
@@ -79,7 +79,7 @@ async def load_remote_conf_def(
|
|
|
79
79
|
async with aopen(config_file, mode="r") as f:
|
|
80
80
|
raw_json = await f.read()
|
|
81
81
|
|
|
82
|
-
return RemoteConfigDefinition.
|
|
82
|
+
return RemoteConfigDefinition.model_validate_json(raw_json)
|
|
83
83
|
except FileNotFoundError:
|
|
84
84
|
logger.error(f"No remote config definition exists at {config_file}")
|
|
85
85
|
except JSONDecodeError:
|
pgbelt/util/pglogical.py
CHANGED
|
@@ -78,7 +78,7 @@ async def grant_pgl(pool: Pool, tables: list[str], schema: str, logger: Logger)
|
|
|
78
78
|
async with pool.acquire() as conn:
|
|
79
79
|
async with conn.transaction():
|
|
80
80
|
if tables:
|
|
81
|
-
tables_with_schema = [f
|
|
81
|
+
tables_with_schema = [f'{schema}."{table}"' for table in tables]
|
|
82
82
|
await conn.execute(
|
|
83
83
|
f"GRANT ALL ON TABLE {','.join(tables_with_schema)} TO pglogical;"
|
|
84
84
|
)
|
pgbelt/util/postgres.py
CHANGED
|
@@ -100,14 +100,12 @@ async def compare_data(
|
|
|
100
100
|
has_run = False
|
|
101
101
|
for table in set(pkeys):
|
|
102
102
|
# If specific table list is defined and the iterated table is not in that list, skip.
|
|
103
|
-
|
|
104
|
-
# map the passed conf tables to lowercase.
|
|
105
|
-
if tables and (table not in list(map(str.lower, tables))):
|
|
103
|
+
if tables and (table not in tables):
|
|
106
104
|
continue
|
|
107
105
|
|
|
108
106
|
has_run = True # If this runs, we have at least one table to compare. We will use this flag to throw an error if no tables are found.
|
|
109
107
|
|
|
110
|
-
full_table_name = f
|
|
108
|
+
full_table_name = f'{schema}."{table}"'
|
|
111
109
|
|
|
112
110
|
logger.debug(f"Validating table {full_table_name}...")
|
|
113
111
|
order_by_pkeys = ",".join(pkeys_dict[table])
|
|
@@ -387,15 +385,9 @@ async def precheck_info(
|
|
|
387
385
|
)
|
|
388
386
|
|
|
389
387
|
# We filter the table list if the user has specified a list of tables to target.
|
|
390
|
-
# Note, from issue #420, the above query will return the table names in lowercase,
|
|
391
|
-
# so we need to map the target_tables to lowercase.
|
|
392
388
|
if target_tables:
|
|
393
389
|
|
|
394
|
-
result["tables"] = [
|
|
395
|
-
t
|
|
396
|
-
for t in result["tables"]
|
|
397
|
-
if t["Name"] in list(map(str.lower, target_tables))
|
|
398
|
-
]
|
|
390
|
+
result["tables"] = [t for t in result["tables"] if t["Name"] in target_tables]
|
|
399
391
|
|
|
400
392
|
# We will not recapitalize the table names in the result["tables"] list,
|
|
401
393
|
# to preserve how Postgres sees those tables in its system catalog. Easy
|
|
@@ -419,14 +411,10 @@ async def precheck_info(
|
|
|
419
411
|
)
|
|
420
412
|
|
|
421
413
|
# We filter the table list if the user has specified a list of tables to target.
|
|
422
|
-
# Note, from issue #420, the above query will return the table names in lowercase,
|
|
423
|
-
# so we need to map the target_tables to lowercase.
|
|
424
414
|
if target_sequences:
|
|
425
415
|
|
|
426
416
|
result["sequences"] = [
|
|
427
|
-
t
|
|
428
|
-
for t in result["sequences"]
|
|
429
|
-
if t["Name"] in list(map(str.lower, target_sequences))
|
|
417
|
+
t for t in result["sequences"] if t["Name"] in target_sequences
|
|
430
418
|
]
|
|
431
419
|
|
|
432
420
|
# We will not recapitalize the table names in the result["tables"] list,
|
|
@@ -485,7 +473,7 @@ async def get_dataset_size(
|
|
|
485
473
|
|
|
486
474
|
query = f"""
|
|
487
475
|
SELECT
|
|
488
|
-
sum(pg_total_relation_size(schemaname || '.' || tablename)) AS total_relation_size
|
|
476
|
+
sum(pg_total_relation_size(schemaname || '."' || tablename || '"')) AS total_relation_size
|
|
489
477
|
FROM
|
|
490
478
|
pg_tables
|
|
491
479
|
WHERE
|
|
@@ -496,7 +484,7 @@ async def get_dataset_size(
|
|
|
496
484
|
# Yes it's a duplicate, but it's a pretty one. Rather let Postgres do this than Python.
|
|
497
485
|
pretty_query = f"""
|
|
498
486
|
SELECT
|
|
499
|
-
pg_size_pretty(sum(pg_total_relation_size(schemaname || '.' || tablename))) AS total_relation_size
|
|
487
|
+
pg_size_pretty(sum(pg_total_relation_size(schemaname || '."' || tablename || '"'))) AS total_relation_size
|
|
500
488
|
FROM
|
|
501
489
|
pg_tables
|
|
502
490
|
WHERE
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pgbelt
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.4
|
|
4
4
|
Summary: A CLI tool used to manage Postgres data migrations from beginning to end, for a single database or a fleet, leveraging pglogical replication.
|
|
5
5
|
Author: Varjitt Jeeva
|
|
6
6
|
Author-email: varjitt.jeeva@autodesk.com
|
|
@@ -14,7 +14,7 @@ Requires-Dist: aiofiles (>=0.8,<23.3)
|
|
|
14
14
|
Requires-Dist: asyncpg (>=0.27,<0.30)
|
|
15
15
|
Requires-Dist: pydantic (>=2.0,<3.0)
|
|
16
16
|
Requires-Dist: tabulate (>=0.9.0,<0.10.0)
|
|
17
|
-
Requires-Dist: typer (>=0.9
|
|
17
|
+
Requires-Dist: typer (>=0.9,<0.13)
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
|
|
20
20
|
# Pgbelt
|
|
@@ -5,23 +5,23 @@ pgbelt/cmd/helpers.py,sha256=anMAkZjJRy7n0EwewyMNJGJ4Jx9keSNZdzWqi3ICNgI,5292
|
|
|
5
5
|
pgbelt/cmd/login.py,sha256=kyDT755YsScg3QA4ZlkyIq3J0WO4XqMepMchufjuaDE,3043
|
|
6
6
|
pgbelt/cmd/preflight.py,sha256=-78Puqqf1rCxNQEyn4bQIAORez_tsy6zJAbSuO_de9s,20676
|
|
7
7
|
pgbelt/cmd/schema.py,sha256=XAAj2BH6HVoA1LxuWw2kheesGDu41L3GAd5UahNp3nE,4760
|
|
8
|
-
pgbelt/cmd/setup.py,sha256=
|
|
9
|
-
pgbelt/cmd/status.py,sha256=
|
|
10
|
-
pgbelt/cmd/sync.py,sha256=
|
|
8
|
+
pgbelt/cmd/setup.py,sha256=Jp5sqT9_whoVBiOzAlOzX1ubtXQADYBkBrJldch_fKk,6627
|
|
9
|
+
pgbelt/cmd/status.py,sha256=LTvteFwA1jcfnGJdlA2siJn5TUtlf14ffMkGVSi5gi0,4730
|
|
10
|
+
pgbelt/cmd/sync.py,sha256=8TSNbn1dpkyxlEMImY7FjOrzhriSXlorqE-HBjqbUfU,9027
|
|
11
11
|
pgbelt/cmd/teardown.py,sha256=TTSmhmD1bqJ8-P5YyK5ZpCsmneczO0nvd2Q2s2oyXY4,3526
|
|
12
12
|
pgbelt/config/__init__.py,sha256=SXok1aZcpMYJpX_hk5cuKO33CJ5s8IESkswNN9KsVSo,35
|
|
13
13
|
pgbelt/config/config.py,sha256=Kw2H-G1Evfj0TXIbh3k06gE72dZEp_wXWJ2Icq_T54c,3817
|
|
14
|
-
pgbelt/config/models.py,sha256=
|
|
15
|
-
pgbelt/config/remote.py,sha256=
|
|
14
|
+
pgbelt/config/models.py,sha256=YnsBYAa5hC-hcrbgd0-rYcpYYc4oU-cq7sRy0RsudY0,6357
|
|
15
|
+
pgbelt/config/remote.py,sha256=D9bOekVfMU1xX2Wy0OiJwSXetxJUdt9Tn5Fukwn9rnE,5307
|
|
16
16
|
pgbelt/main.py,sha256=YiagBiGt8pbNlukkRxROXnQX1Tx6ax7c6riuHRCrPYU,186
|
|
17
17
|
pgbelt/util/__init__.py,sha256=-6KkvVMz-yGNQfeoo4CZZrgWKXYmFd4CMyoiao8OnFE,40
|
|
18
18
|
pgbelt/util/asyncfuncs.py,sha256=7i_GpBmUNNZ8RUGvU-q5nclsoaCm6Lx8jLP8usYvmZc,583
|
|
19
19
|
pgbelt/util/dump.py,sha256=AwyOAd9CP014gvsl-qlo1lbnXZfxoeN4ujZWUIq7KM8,14715
|
|
20
20
|
pgbelt/util/logs.py,sha256=l2jT-WKZ-33eNDw4S4W1_eE4ISo4rtDRXYLVf4QTV4Y,1699
|
|
21
|
-
pgbelt/util/pglogical.py,sha256=
|
|
22
|
-
pgbelt/util/postgres.py,sha256=
|
|
23
|
-
pgbelt-0.7.
|
|
24
|
-
pgbelt-0.7.
|
|
25
|
-
pgbelt-0.7.
|
|
26
|
-
pgbelt-0.7.
|
|
27
|
-
pgbelt-0.7.
|
|
21
|
+
pgbelt/util/pglogical.py,sha256=Y6KZBeiH85zhNSvhATqh0xozhfUMyQnPWN1HwRosZFo,13613
|
|
22
|
+
pgbelt/util/postgres.py,sha256=jNMYhkb4OHYhNzDu7Fppz8wrXogAw1XxVhxdBA2e6cI,19270
|
|
23
|
+
pgbelt-0.7.4.dist-info/LICENSE,sha256=FQ5cFkW02dKK3LmKH8z-rwn93tWSCh7lsxfNUiWcFsg,10758
|
|
24
|
+
pgbelt-0.7.4.dist-info/METADATA,sha256=9sf3BPo7Iq4P2yIpeDbA8ArZUKhg6Yo29V5fTuixQE4,2960
|
|
25
|
+
pgbelt-0.7.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
26
|
+
pgbelt-0.7.4.dist-info/entry_points.txt,sha256=SCz_poPjkaVnWpJ-CeytAnDzbVc6l0WalOwitIqW_3g,40
|
|
27
|
+
pgbelt-0.7.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|