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 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 validator
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 = validator("name", "pw", allow_reuse=True)(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 = validator("host", "ip", "db", "port", allow_reuse=True)(not_empty)
67
+ _not_empty = field_validator("host", "ip", "db", "port")(not_empty)
68
68
 
69
- @validator("root_user", "owner_user", "pglogical_user")
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 = validator("db", "dc", allow_reuse=True)(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.parse_raw(raw)
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.parse_raw(raw_json)
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"{schema}.{table}" for table in tables]
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
- full_table_name = f"{schema}.{table}"
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 sequence list if the user has specified a list of sequences to target.
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
- s for s in result["sequences"] if s["Name"] in target_sequences
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.1
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.0,<0.10.0)
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=CYaRGQG_ooz0BpP5g1YVmPvt8zcT4DSIdQM6nASr9Qc,5908
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=4qm23kdNNZiQOZrejD1yXP6h0Z38LNFSyI4d0i-IdUs,5830
15
- pgbelt/config/remote.py,sha256=RQ_dfL5g2ChVP6jeGWnpQMVKQZK1o2m9-ZPYBMaaGj4,5297
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=r4I95CG4eRFsEPnN30P4yxt8HeGMzKBTEm3fecfw4ws,13611
22
- pgbelt/util/postgres.py,sha256=909eSLhnhAkGe_4IVt-j72I3wXHArT5V_YikJnl6gdc,18178
23
- pgbelt-0.7.1.dist-info/LICENSE,sha256=FQ5cFkW02dKK3LmKH8z-rwn93tWSCh7lsxfNUiWcFsg,10758
24
- pgbelt-0.7.1.dist-info/METADATA,sha256=uXZgpAXeisAYU3M89TVK_w18R3VaEKteXZVQgQB_Kg8,2964
25
- pgbelt-0.7.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
26
- pgbelt-0.7.1.dist-info/entry_points.txt,sha256=SCz_poPjkaVnWpJ-CeytAnDzbVc6l0WalOwitIqW_3g,40
27
- pgbelt-0.7.1.dist-info/RECORD,,
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