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 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 validator
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 = validator("name", "pw", allow_reuse=True)(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 = validator("host", "ip", "db", "port", allow_reuse=True)(not_empty)
68
+ _not_empty = field_validator("host", "ip", "db", "port")(not_empty)
68
69
 
69
- @validator("root_user", "owner_user", "pglogical_user")
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
- return f"postgresql://{self.root_user.name}:{self.root_user.pw}@{self.ip}:{self.port}/{self.db}"
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
- return f"postgresql://{self.owner_user.name}:{self.owner_user.pw}@{self.ip}:{self.port}/{self.db}"
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
- return f"postgresql://{self.pglogical_user.name}:{self.pglogical_user.pw}@{self.ip}:{self.port}/{self.db}"
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 = validator("db", "dc", allow_reuse=True)(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.parse_raw(raw)
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.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
@@ -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
- # Note that the pkeys tables returned from Postgres are all lowercased, so we need to
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"{schema}.{table}"
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.2
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.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=eNeZ9BIVJw_k-a0hdW6L5f9aSi3jW_l1M-7KsVdE3kg,7015
9
- pgbelt/cmd/status.py,sha256=iZE1QnEG7dpZUikFmBUeYddiCGAbfA0Sbx0neDtdn1M,4905
10
- pgbelt/cmd/sync.py,sha256=kmcpYcJVlhQo0CtUzZ97a0A6juRFiQi01l0JyV41YeI,9415
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=4qm23kdNNZiQOZrejD1yXP6h0Z38LNFSyI4d0i-IdUs,5830
15
- pgbelt/config/remote.py,sha256=RQ_dfL5g2ChVP6jeGWnpQMVKQZK1o2m9-ZPYBMaaGj4,5297
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=r4I95CG4eRFsEPnN30P4yxt8HeGMzKBTEm3fecfw4ws,13611
22
- pgbelt/util/postgres.py,sha256=esLdL6l2OZv_XaSRNz2v9fCXTuqr5keW_EX1PLR_PHc,19819
23
- pgbelt-0.7.2.dist-info/LICENSE,sha256=FQ5cFkW02dKK3LmKH8z-rwn93tWSCh7lsxfNUiWcFsg,10758
24
- pgbelt-0.7.2.dist-info/METADATA,sha256=I4CcfjD18dUp0DhePF0u9zF7aE8lMrg65HfnrAb1kKE,2964
25
- pgbelt-0.7.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
26
- pgbelt-0.7.2.dist-info/entry_points.txt,sha256=SCz_poPjkaVnWpJ-CeytAnDzbVc6l0WalOwitIqW_3g,40
27
- pgbelt-0.7.2.dist-info/RECORD,,
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