pgbelt 0.7.1__py3-none-any.whl → 0.7.3__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 +9 -0
- pgbelt/config/models.py +6 -6
- pgbelt/config/remote.py +1 -1
- pgbelt/util/pglogical.py +1 -1
- pgbelt/util/postgres.py +30 -6
- {pgbelt-0.7.1.dist-info → pgbelt-0.7.3.dist-info}/METADATA +2 -2
- {pgbelt-0.7.1.dist-info → pgbelt-0.7.3.dist-info}/RECORD +10 -10
- {pgbelt-0.7.1.dist-info → pgbelt-0.7.3.dist-info}/LICENSE +0 -0
- {pgbelt-0.7.1.dist-info → pgbelt-0.7.3.dist-info}/WHEEL +0 -0
- {pgbelt-0.7.1.dist-info → pgbelt-0.7.3.dist-info}/entry_points.txt +0 -0
pgbelt/cmd/setup.py
CHANGED
|
@@ -43,6 +43,15 @@ async def _setup_src_node(
|
|
|
43
43
|
if conf.tables:
|
|
44
44
|
pglogical_tables = [t for t in pkey_tables if t in conf.tables]
|
|
45
45
|
|
|
46
|
+
# Intentionally throw an error if no tables are found, so that the user can correct their config.
|
|
47
|
+
# When reported by a certain user, errors showed when running the status command, but it was ignored,
|
|
48
|
+
# then the user ran setup and since that DIDN'T throw an error, they assumed everything was fine.
|
|
49
|
+
|
|
50
|
+
if not pglogical_tables:
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"No tables were targeted to replicate. Please check your config's schema and tables. DB: {conf.db} DC: {conf.dc}, SCHEMA: {conf.schema_name} TABLES: {conf.tables}.\nIf TABLES is [], all tables in the schema should be replicated, but pgbelt still found no tables.\nCheck the schema name or reach out to the pgbelt team for help."
|
|
53
|
+
)
|
|
54
|
+
|
|
46
55
|
await configure_replication_set(
|
|
47
56
|
src_root_pool, pglogical_tables, conf.schema_name, src_logger
|
|
48
57
|
)
|
pgbelt/config/models.py
CHANGED
|
@@ -9,7 +9,7 @@ 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
13
|
|
|
14
14
|
|
|
15
15
|
def config_dir(db: str, dc: str) -> str:
|
|
@@ -37,7 +37,7 @@ class User(BaseModel):
|
|
|
37
37
|
name: str
|
|
38
38
|
pw: Optional[str] = None
|
|
39
39
|
|
|
40
|
-
_not_empty =
|
|
40
|
+
_not_empty = field_validator("name", "pw")(not_empty)
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
class DbConfig(BaseModel):
|
|
@@ -64,9 +64,9 @@ class DbConfig(BaseModel):
|
|
|
64
64
|
pglogical_user: User
|
|
65
65
|
other_users: Optional[list[User]] = None
|
|
66
66
|
|
|
67
|
-
_not_empty =
|
|
67
|
+
_not_empty = field_validator("host", "ip", "db", "port")(not_empty)
|
|
68
68
|
|
|
69
|
-
@
|
|
69
|
+
@field_validator("root_user", "owner_user", "pglogical_user")
|
|
70
70
|
def has_password(cls, v) -> User: # noqa: N805
|
|
71
71
|
if not v.pw:
|
|
72
72
|
raise ValueError
|
|
@@ -118,7 +118,7 @@ class DbupgradeConfig(BaseModel):
|
|
|
118
118
|
sequences: Optional[list[str]] = None
|
|
119
119
|
schema_name: Optional[str] = "public"
|
|
120
120
|
|
|
121
|
-
_not_empty =
|
|
121
|
+
_not_empty = field_validator("db", "dc")(not_empty)
|
|
122
122
|
|
|
123
123
|
@property
|
|
124
124
|
def file(self) -> str:
|
|
@@ -167,7 +167,7 @@ class DbupgradeConfig(BaseModel):
|
|
|
167
167
|
return None
|
|
168
168
|
|
|
169
169
|
try:
|
|
170
|
-
out = cls.
|
|
170
|
+
out = cls.model_validate_json(raw)
|
|
171
171
|
except ValidationError:
|
|
172
172
|
logger.info("Cached config was not a valid DbupgradeConfig")
|
|
173
173
|
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
|
@@ -97,11 +97,15 @@ async def compare_data(
|
|
|
97
97
|
dst_old_extra_float_digits = await dst_pool.fetchval("SHOW extra_float_digits;")
|
|
98
98
|
await dst_pool.execute("SET extra_float_digits TO 0;")
|
|
99
99
|
|
|
100
|
+
has_run = False
|
|
100
101
|
for table in set(pkeys):
|
|
101
|
-
# If specific table list is defined and iterated table is not in that list, skip.
|
|
102
|
+
# If specific table list is defined and the iterated table is not in that list, skip.
|
|
102
103
|
if tables and (table not in tables):
|
|
103
104
|
continue
|
|
104
|
-
|
|
105
|
+
|
|
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.
|
|
107
|
+
|
|
108
|
+
full_table_name = f'{schema}."{table}"'
|
|
105
109
|
|
|
106
110
|
logger.debug(f"Validating table {full_table_name}...")
|
|
107
111
|
order_by_pkeys = ",".join(pkeys_dict[table])
|
|
@@ -171,6 +175,13 @@ async def compare_data(
|
|
|
171
175
|
f"Dest Row: {dst_row}"
|
|
172
176
|
)
|
|
173
177
|
|
|
178
|
+
# Just a paranoia check. If this throws, then it's possible pgbelt didn't migrate any data.
|
|
179
|
+
# This was found in issue #420, and previous commands threw errors before this issue could arise.
|
|
180
|
+
if not has_run:
|
|
181
|
+
raise ValueError(
|
|
182
|
+
"No tables were found to compare. Please reach out to the pgbelt for help, and check if your data was migrated."
|
|
183
|
+
)
|
|
184
|
+
|
|
174
185
|
await src_pool.execute(f"SET extra_float_digits TO {src_old_extra_float_digits};")
|
|
175
186
|
await dst_pool.execute(f"SET extra_float_digits TO {dst_old_extra_float_digits};")
|
|
176
187
|
logger.info(
|
|
@@ -372,10 +383,17 @@ async def precheck_info(
|
|
|
372
383
|
AND n.nspname <> 'pglogical'
|
|
373
384
|
ORDER BY 1,2;"""
|
|
374
385
|
)
|
|
386
|
+
|
|
375
387
|
# We filter the table list if the user has specified a list of tables to target.
|
|
376
388
|
if target_tables:
|
|
389
|
+
|
|
377
390
|
result["tables"] = [t for t in result["tables"] if t["Name"] in target_tables]
|
|
378
391
|
|
|
392
|
+
# We will not recapitalize the table names in the result["tables"] list,
|
|
393
|
+
# to preserve how Postgres sees those tables in its system catalog. Easy
|
|
394
|
+
# rabbit hole later if we keep patching the table names to match the user's
|
|
395
|
+
# input.
|
|
396
|
+
|
|
379
397
|
result["sequences"] = await pool.fetch(
|
|
380
398
|
"""
|
|
381
399
|
SELECT n.nspname as "Schema",
|
|
@@ -392,12 +410,18 @@ async def precheck_info(
|
|
|
392
410
|
ORDER BY 1,2;"""
|
|
393
411
|
)
|
|
394
412
|
|
|
395
|
-
# We filter the
|
|
413
|
+
# We filter the table list if the user has specified a list of tables to target.
|
|
396
414
|
if target_sequences:
|
|
415
|
+
|
|
397
416
|
result["sequences"] = [
|
|
398
|
-
|
|
417
|
+
t for t in result["sequences"] if t["Name"] in target_sequences
|
|
399
418
|
]
|
|
400
419
|
|
|
420
|
+
# We will not recapitalize the table names in the result["tables"] list,
|
|
421
|
+
# to preserve how Postgres sees those tables in its system catalog. Easy
|
|
422
|
+
# rabbit hole later if we keep patching the table names to match the user's
|
|
423
|
+
# input.
|
|
424
|
+
|
|
401
425
|
users = await pool.fetch(
|
|
402
426
|
f"""
|
|
403
427
|
SELECT r.rolname, r.rolsuper, r.rolinherit,
|
|
@@ -449,7 +473,7 @@ async def get_dataset_size(
|
|
|
449
473
|
|
|
450
474
|
query = f"""
|
|
451
475
|
SELECT
|
|
452
|
-
sum(pg_total_relation_size(schemaname || '.' || tablename)) AS total_relation_size
|
|
476
|
+
sum(pg_total_relation_size(schemaname || '."' || tablename || '"')) AS total_relation_size
|
|
453
477
|
FROM
|
|
454
478
|
pg_tables
|
|
455
479
|
WHERE
|
|
@@ -460,7 +484,7 @@ async def get_dataset_size(
|
|
|
460
484
|
# Yes it's a duplicate, but it's a pretty one. Rather let Postgres do this than Python.
|
|
461
485
|
pretty_query = f"""
|
|
462
486
|
SELECT
|
|
463
|
-
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
|
|
464
488
|
FROM
|
|
465
489
|
pg_tables
|
|
466
490
|
WHERE
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pgbelt
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
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=
|
|
8
|
+
pgbelt/cmd/setup.py,sha256=Jp5sqT9_whoVBiOzAlOzX1ubtXQADYBkBrJldch_fKk,6627
|
|
9
9
|
pgbelt/cmd/status.py,sha256=LTvteFwA1jcfnGJdlA2siJn5TUtlf14ffMkGVSi5gi0,4730
|
|
10
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=4MeeQ_puAZtMVix8Cj5GNCtYTUWn5lHHijS82yhD4mg,5816
|
|
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.3.dist-info/LICENSE,sha256=FQ5cFkW02dKK3LmKH8z-rwn93tWSCh7lsxfNUiWcFsg,10758
|
|
24
|
+
pgbelt-0.7.3.dist-info/METADATA,sha256=DmeWJ6E8SdjC36sTp8qPHC73_6LWd5GSqN2tdW_BoWI,2960
|
|
25
|
+
pgbelt-0.7.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
26
|
+
pgbelt-0.7.3.dist-info/entry_points.txt,sha256=SCz_poPjkaVnWpJ-CeytAnDzbVc6l0WalOwitIqW_3g,40
|
|
27
|
+
pgbelt-0.7.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|