execsql2 2.20.0__py3-none-any.whl → 2.21.1__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.
Files changed (22) hide show
  1. execsql/db/factory.py +1 -1
  2. execsql/db/postgres.py +28 -18
  3. {execsql2-2.20.0.dist-info → execsql2-2.21.1.dist-info}/METADATA +7 -7
  4. {execsql2-2.20.0.dist-info → execsql2-2.21.1.dist-info}/RECORD +22 -22
  5. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/README.md +0 -0
  6. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/config_settings.sqlite +0 -0
  7. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
  8. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/make_config_db.sql +0 -0
  9. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/md_compare.sql +0 -0
  10. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/md_glossary.sql +0 -0
  11. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/md_upsert.sql +0 -0
  12. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/pg_compare.sql +0 -0
  13. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/pg_glossary.sql +0 -0
  14. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/pg_upsert.sql +0 -0
  15. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/script_template.sql +0 -0
  16. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/ss_compare.sql +0 -0
  17. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/ss_glossary.sql +0 -0
  18. {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/ss_upsert.sql +0 -0
  19. {execsql2-2.20.0.dist-info → execsql2-2.21.1.dist-info}/WHEEL +0 -0
  20. {execsql2-2.20.0.dist-info → execsql2-2.21.1.dist-info}/entry_points.txt +0 -0
  21. {execsql2-2.20.0.dist-info → execsql2-2.21.1.dist-info}/licenses/LICENSE.txt +0 -0
  22. {execsql2-2.20.0.dist-info → execsql2-2.21.1.dist-info}/licenses/NOTICE +0 -0
execsql/db/factory.py CHANGED
@@ -63,7 +63,7 @@ def db_Postgres(
63
63
  new_db: bool = False,
64
64
  password: str | None = None,
65
65
  ) -> PostgresDatabase:
66
- """Open a new PostgreSQL connection via psycopg2."""
66
+ """Open a new PostgreSQL connection via psycopg (psycopg3)."""
67
67
  return PostgresDatabase(server_name, database_name, user, pw_needed, port, new_db=new_db, password=password)
68
68
 
69
69
 
execsql/db/postgres.py CHANGED
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  """
4
4
  PostgreSQL database adapter for execsql.
5
5
 
6
- Implements :class:`PostgresDatabase`. Uses ``psycopg2`` for the
6
+ Implements :class:`PostgresDatabase`. Uses ``psycopg`` (psycopg3) for the
7
7
  connection, supports schema-qualified tables, server-side ``COPY`` for
8
8
  fast IMPORT, ``CREATE DATABASE`` when ``new_db=True``, ``ROLE_EXISTS``,
9
9
  and the ``PG_VACUUM`` metacommand (``vacuum()`` method). Corresponds to
@@ -26,7 +26,7 @@ DEFAULT_CONNECT_TIMEOUT = 30 # seconds
26
26
 
27
27
 
28
28
  class PostgresDatabase(Database):
29
- """PostgreSQL adapter using psycopg2, with schema support, server-side COPY, and keyring auth."""
29
+ """PostgreSQL adapter using psycopg (psycopg3), with schema support, server-side COPY, and keyring auth."""
30
30
 
31
31
  def __init__(
32
32
  self,
@@ -41,10 +41,11 @@ class PostgresDatabase(Database):
41
41
  connect_timeout: int = DEFAULT_CONNECT_TIMEOUT,
42
42
  ) -> None:
43
43
  try:
44
- import psycopg2 # noqa: F401
44
+ import psycopg # noqa: F401
45
45
  except Exception:
46
46
  fatal_error(
47
- "The psycopg2 module is required to connect to PostgreSQL. See http://initd.org/psycopg/",
47
+ "The psycopg module (psycopg3) is required to connect to PostgreSQL. "
48
+ "See https://www.psycopg.org/psycopg3/",
48
49
  )
49
50
  from execsql.types import dbt_postgres
50
51
 
@@ -73,25 +74,33 @@ class PostgresDatabase(Database):
73
74
 
74
75
  def open_db(self) -> None:
75
76
  """Open a connection to the PostgreSQL database."""
76
- import psycopg2
77
+ import psycopg
77
78
 
78
79
  def db_conn(db: PostgresDatabase, db_name: str):
79
80
  try:
81
+ # prepare_threshold=None disables psycopg3's automatic server-side
82
+ # prepared statements. execsql re-runs the same query text (e.g. via
83
+ # EXPORT) against objects that scripts may drop/recreate or alter
84
+ # between runs; a cached plan whose result type then changes triggers
85
+ # PostgreSQL's "cached plan must not change result type" error. psycopg2
86
+ # never auto-prepared, so this preserves backwards-compatible behavior.
80
87
  if db.user and db.password:
81
- return psycopg2.connect(
88
+ return psycopg.connect(
82
89
  host=str(db.server_name),
83
- database=str(db_name),
90
+ dbname=str(db_name),
84
91
  port=db.port,
85
92
  user=db.user,
86
93
  password=db.password,
87
94
  connect_timeout=db.connect_timeout,
95
+ prepare_threshold=None,
88
96
  )
89
97
  else:
90
- return psycopg2.connect(
98
+ return psycopg.connect(
91
99
  host=str(db.server_name),
92
- database=db_name,
100
+ dbname=db_name,
93
101
  port=db.port,
94
102
  connect_timeout=db.connect_timeout,
103
+ prepare_threshold=None,
95
104
  )
96
105
  except Exception as e:
97
106
  msg = (
@@ -148,7 +157,7 @@ class PostgresDatabase(Database):
148
157
  msg = f"Failed to open PostgreSQL database {self.db_name} on {self.server_name}"
149
158
  raise ErrInfo(type="exception", exception_msg=exception_desc(), other_msg=msg) from e
150
159
  # (Re)set the encoding to match the database.
151
- self.encoding = self.conn.encoding
160
+ self.encoding = self.conn.info.encoding
152
161
 
153
162
  def exec_cmd(self, querycommand: str) -> None:
154
163
  """Execute a stored function by name."""
@@ -242,13 +251,13 @@ class PostgresDatabase(Database):
242
251
  but should not be exposed to untrusted input.
243
252
  """
244
253
  self.commit()
245
- self.conn.set_session(autocommit=True)
254
+ self.conn.autocommit = True
246
255
  curs = self.conn.cursor()
247
256
  try:
248
257
  curs.execute(f"VACUUM {argstring};")
249
258
  finally:
250
259
  curs.close()
251
- self.conn.set_session(autocommit=False)
260
+ self.conn.autocommit = False
252
261
 
253
262
  def import_tabular_file(
254
263
  self,
@@ -290,7 +299,7 @@ class PostgresDatabase(Database):
290
299
  import_cols = [self.type.quoted(col) for col in import_cols]
291
300
  csv_file_cols_q = [self.type.quoted(col) for col in csv_file_cols]
292
301
  input_col_list = ",".join(import_cols)
293
- # If encodings match, use copy_expert.
302
+ # If encodings match, use server-side COPY.
294
303
  # If encodings don't match, and the file encoding isn't recognized by CSV, read as CSV.
295
304
  enc_xlates = {
296
305
  "cp1252": "win1252",
@@ -319,7 +328,7 @@ class PostgresDatabase(Database):
319
328
  and not _state.conf.trim_strings
320
329
  and not _state.conf.replace_newlines
321
330
  ):
322
- # Use Postgres' COPY FROM method via psycopg2's copy_expert() method.
331
+ # Use Postgres' COPY FROM method via psycopg3's cursor.copy() context manager.
323
332
  rf = csv_file_obj.open("rt")
324
333
  if skipheader:
325
334
  next(rf)
@@ -346,7 +355,9 @@ class PostgresDatabase(Database):
346
355
  )
347
356
  with self._cursor() as curs:
348
357
  try:
349
- curs.copy_expert(copy_cmd, rf, _state.conf.import_buffer)
358
+ with curs.copy(copy_cmd) as copy:
359
+ while chunk := rf.read(_state.conf.import_buffer):
360
+ copy.write(chunk)
350
361
  except ErrInfo:
351
362
  raise
352
363
  except Exception as e:
@@ -461,12 +472,11 @@ class PostgresDatabase(Database):
461
472
  file_name: str,
462
473
  ) -> None:
463
474
  """Import an entire binary file into a single column of a table."""
464
- import psycopg2
465
-
466
475
  with open(file_name, "rb") as f:
467
476
  filedata = f.read()
468
477
  sq_name = self.schema_qualified_table_name(schema_name, table_name)
469
478
  quoted_col = self.quote_identifier(column_name)
470
479
  sql = f"insert into {sq_name} ({quoted_col}) values ({self.paramsubs(1)});"
471
480
  with self._cursor() as curs:
472
- curs.execute(sql, (psycopg2.Binary(filedata),))
481
+ # psycopg3 sends ``bytes`` to a ``bytea`` column directly; no Binary() wrapper.
482
+ curs.execute(sql, (filedata,))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: execsql2
3
- Version: 2.20.0
3
+ Version: 2.21.1
4
4
  Summary: Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables.
5
5
  Project-URL: Homepage, https://execsql2.readthedocs.io
6
6
  Project-URL: Repository, https://github.com/geocoug/execsql
@@ -52,9 +52,9 @@ Requires-Dist: keyring>=25.0; extra == 'all'
52
52
  Requires-Dist: odfpy>=1.4; extra == 'all'
53
53
  Requires-Dist: openpyxl>=3.1; extra == 'all'
54
54
  Requires-Dist: oracledb>=3.0; extra == 'all'
55
- Requires-Dist: pg-upsert>=1.22.1; extra == 'all'
55
+ Requires-Dist: pg-upsert>=1.23.0; extra == 'all'
56
56
  Requires-Dist: polars>=1.0; extra == 'all'
57
- Requires-Dist: psycopg2-binary>=2.9; extra == 'all'
57
+ Requires-Dist: psycopg[binary]<4,>=3.1; extra == 'all'
58
58
  Requires-Dist: pymysql>=1.1; extra == 'all'
59
59
  Requires-Dist: pyodbc>=5.0; extra == 'all'
60
60
  Requires-Dist: pyyaml>=6.0; extra == 'all'
@@ -66,7 +66,7 @@ Provides-Extra: all-db
66
66
  Requires-Dist: duckdb>=1.0; extra == 'all-db'
67
67
  Requires-Dist: firebird-driver>=1.10; extra == 'all-db'
68
68
  Requires-Dist: oracledb>=3.0; extra == 'all-db'
69
- Requires-Dist: psycopg2-binary>=2.9; extra == 'all-db'
69
+ Requires-Dist: psycopg[binary]<4,>=3.1; extra == 'all-db'
70
70
  Requires-Dist: pymysql>=1.1; extra == 'all-db'
71
71
  Requires-Dist: pyodbc>=5.0; extra == 'all-db'
72
72
  Provides-Extra: auth
@@ -124,9 +124,9 @@ Requires-Dist: pyodbc>=5.0; extra == 'odbc'
124
124
  Provides-Extra: oracle
125
125
  Requires-Dist: oracledb>=3.0; extra == 'oracle'
126
126
  Provides-Extra: postgres
127
- Requires-Dist: psycopg2-binary>=2.9; extra == 'postgres'
127
+ Requires-Dist: psycopg[binary]<4,>=3.1; extra == 'postgres'
128
128
  Provides-Extra: upsert
129
- Requires-Dist: pg-upsert>=1.22.1; extra == 'upsert'
129
+ Requires-Dist: pg-upsert>=1.23.0; extra == 'upsert'
130
130
  Description-Content-Type: text/markdown
131
131
 
132
132
  > [!NOTE]
@@ -403,7 +403,7 @@ execsql-format --no-sql --in-place scripts/
403
403
  ```yaml
404
404
  repos:
405
405
  - repo: https://github.com/geocoug/execsql
406
- rev: v2.20.0
406
+ rev: v2.21.1
407
407
  hooks:
408
408
  - id: execsql-format
409
409
  ```
@@ -22,11 +22,11 @@ execsql/db/access.py,sha256=sRJOJJo1psSO5RV6QPsFY1YJE5wc5aNOYCFDtxIn_Wo,19413
22
22
  execsql/db/base.py,sha256=0Cy_M3GHdkQDJMbMeWpNyUIPux9m1cVDy21MGMjhJCI,35002
23
23
  execsql/db/dsn.py,sha256=CZd5NhUSVvK3irv2maeWsSqBsZjhFsTxIuT29Z0rP2A,6240
24
24
  execsql/db/duckdb.py,sha256=79lRzKRhw1Pjfqcrba27S4Oq8a8AbDO_d0XkaNKKPQo,3197
25
- execsql/db/factory.py,sha256=YHdgyqQYy16548O3fGyElLC5C7DdIgva4Z29OsDxXjs,5367
25
+ execsql/db/factory.py,sha256=C_cyi3GjNx0opuwc6maFFeCSkjfOfoUjuf68YT4VAHE,5377
26
26
  execsql/db/firebird.py,sha256=cV5wUzQHoz7yugHdNxAn8IW7fL3v1riIsGLykCZhZwA,9480
27
27
  execsql/db/mysql.py,sha256=Z0vI_nQ6vhG3sYa2fv5NOO3JmAsH6_jVjM8FuujQpqI,17961
28
28
  execsql/db/oracle.py,sha256=1_odb5xmlm8vjdJdQXz7SHm9dzIbZ5sxP_IxTklH3kA,12018
29
- execsql/db/postgres.py,sha256=UNzrXzMniEyT3Z7qjCA_HLEUY0PVr1cJShuhAxtl5l0,21241
29
+ execsql/db/postgres.py,sha256=LIZgx4T_UPZvOk_mn0l832ShNKgzculdOrVrFO7Rj8I,22045
30
30
  execsql/db/sqlite.py,sha256=xooU6bvD9Y3frRpnbyesE63r6E1fwEHkkcN1YD_UIUE,11519
31
31
  execsql/db/sqlserver.py,sha256=j2ViLoBWzizgaL0u6V4iHfjinrlJ4rpBD3XZiKKdwSU,8968
32
32
  execsql/debug/__init__.py,sha256=j6EGUR0dHzUhWN1mHHtf1-Lhjq3Sb1V-vmnq2Ztgj1M,178
@@ -100,23 +100,23 @@ execsql/utils/numeric.py,sha256=xh02ANSRk3nUpQ-rtm66ILoMqoi7HtzCoRMIOT9U8QI,1570
100
100
  execsql/utils/regex.py,sha256=diEzTZqU_HHwVMadPAvN1Vgzhl7I03eVaEFGCXyGGL8,3770
101
101
  execsql/utils/strings.py,sha256=UQNjpRCEFa1UO6feU-M-9e24wWAvizs_iu_4fFusLxo,8516
102
102
  execsql/utils/timer.py,sha256=eDYf5VzCNFk7oo90InJucUm3XcBdhYMogjZMqeg9xzc,1899
103
- execsql2-2.20.0.data/data/execsql2_extras/README.md,sha256=vX4NTL095dUoA_hesyRMGYBorEZ_Y_tJ9qrd-MVV09I,5032
104
- execsql2-2.20.0.data/data/execsql2_extras/config_settings.sqlite,sha256=aY5cxR7Q7J6zJ4bC9lu5mHUrhy211Cq3MNKPQVCt02E,20480
105
- execsql2-2.20.0.data/data/execsql2_extras/example_config_prompt.sql,sha256=2e8KzzVWhho8KxYVHETSVmZdhW7wodioDsqBLSL6m4s,7487
106
- execsql2-2.20.0.data/data/execsql2_extras/make_config_db.sql,sha256=WwyC6dK-Eh5CAVppiBCDHqiI1_wEI9U95Ytpr4lsZkg,8726
107
- execsql2-2.20.0.data/data/execsql2_extras/md_compare.sql,sha256=qYYVAjSeHZzjszxV3Bv6bg8Ckbq2kMHl87_gh4sywMU,24140
108
- execsql2-2.20.0.data/data/execsql2_extras/md_glossary.sql,sha256=hkZ2Onn57LAKKsuXxzhR8tPtcWXkmWEQkwPE58-Tm2k,10796
109
- execsql2-2.20.0.data/data/execsql2_extras/md_upsert.sql,sha256=_CAK4BzEboRXTNy03SJR-oOjcEdSNMuRBPL6noWUptY,112560
110
- execsql2-2.20.0.data/data/execsql2_extras/pg_compare.sql,sha256=1zJd4hVUKHR0tncc2qTBC9B4qVV4Us2ITkJpsjN3tMw,24352
111
- execsql2-2.20.0.data/data/execsql2_extras/pg_glossary.sql,sha256=IKuwna-_8b20ljSkXZruuiQigrCpo7ueQdUqd1MXiuI,9908
112
- execsql2-2.20.0.data/data/execsql2_extras/pg_upsert.sql,sha256=HpPJtTHvpEjQy03j-3iPxDEOHMRkudOg7O4D4YR38UI,108315
113
- execsql2-2.20.0.data/data/execsql2_extras/script_template.sql,sha256=2J35ddZPguJ-vwTsz83wErv0jiWUyJcdW_JM0mNKDXA,11155
114
- execsql2-2.20.0.data/data/execsql2_extras/ss_compare.sql,sha256=j1qVNUPXQsEU7-DoVgDJCGcE0EuIl7whLBT3fgeiMAo,24833
115
- execsql2-2.20.0.data/data/execsql2_extras/ss_glossary.sql,sha256=2gLxv34xzKt0vy7hSzJH7a9JiMC3ETrv9MofxQwAibU,13065
116
- execsql2-2.20.0.data/data/execsql2_extras/ss_upsert.sql,sha256=G_8rQ0VzuKIZHWs24O_WrfzpC5S27R1JsL-bFBR3SUQ,117730
117
- execsql2-2.20.0.dist-info/METADATA,sha256=2j1tQ7oxlDAfsmIPQwgDxxn9O34FN7MbKT1nLznLi-M,22603
118
- execsql2-2.20.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
119
- execsql2-2.20.0.dist-info/entry_points.txt,sha256=sUOxkM-dN1eBGGpSpDLsAaE0yNXYQKWZAfxPOlMkQyk,90
120
- execsql2-2.20.0.dist-info/licenses/LICENSE.txt,sha256=LBdhuxejF8_bLCHZ2kWfmDXpDGUu914Gbd6_3JjCRe0,676
121
- execsql2-2.20.0.dist-info/licenses/NOTICE,sha256=McYzgxYav3U1OaVsY4Su1sfBrfmplpRdA9b6-gCDQCg,342
122
- execsql2-2.20.0.dist-info/RECORD,,
103
+ execsql2-2.21.1.data/data/execsql2_extras/README.md,sha256=vX4NTL095dUoA_hesyRMGYBorEZ_Y_tJ9qrd-MVV09I,5032
104
+ execsql2-2.21.1.data/data/execsql2_extras/config_settings.sqlite,sha256=aY5cxR7Q7J6zJ4bC9lu5mHUrhy211Cq3MNKPQVCt02E,20480
105
+ execsql2-2.21.1.data/data/execsql2_extras/example_config_prompt.sql,sha256=2e8KzzVWhho8KxYVHETSVmZdhW7wodioDsqBLSL6m4s,7487
106
+ execsql2-2.21.1.data/data/execsql2_extras/make_config_db.sql,sha256=WwyC6dK-Eh5CAVppiBCDHqiI1_wEI9U95Ytpr4lsZkg,8726
107
+ execsql2-2.21.1.data/data/execsql2_extras/md_compare.sql,sha256=qYYVAjSeHZzjszxV3Bv6bg8Ckbq2kMHl87_gh4sywMU,24140
108
+ execsql2-2.21.1.data/data/execsql2_extras/md_glossary.sql,sha256=hkZ2Onn57LAKKsuXxzhR8tPtcWXkmWEQkwPE58-Tm2k,10796
109
+ execsql2-2.21.1.data/data/execsql2_extras/md_upsert.sql,sha256=_CAK4BzEboRXTNy03SJR-oOjcEdSNMuRBPL6noWUptY,112560
110
+ execsql2-2.21.1.data/data/execsql2_extras/pg_compare.sql,sha256=1zJd4hVUKHR0tncc2qTBC9B4qVV4Us2ITkJpsjN3tMw,24352
111
+ execsql2-2.21.1.data/data/execsql2_extras/pg_glossary.sql,sha256=IKuwna-_8b20ljSkXZruuiQigrCpo7ueQdUqd1MXiuI,9908
112
+ execsql2-2.21.1.data/data/execsql2_extras/pg_upsert.sql,sha256=HpPJtTHvpEjQy03j-3iPxDEOHMRkudOg7O4D4YR38UI,108315
113
+ execsql2-2.21.1.data/data/execsql2_extras/script_template.sql,sha256=2J35ddZPguJ-vwTsz83wErv0jiWUyJcdW_JM0mNKDXA,11155
114
+ execsql2-2.21.1.data/data/execsql2_extras/ss_compare.sql,sha256=j1qVNUPXQsEU7-DoVgDJCGcE0EuIl7whLBT3fgeiMAo,24833
115
+ execsql2-2.21.1.data/data/execsql2_extras/ss_glossary.sql,sha256=2gLxv34xzKt0vy7hSzJH7a9JiMC3ETrv9MofxQwAibU,13065
116
+ execsql2-2.21.1.data/data/execsql2_extras/ss_upsert.sql,sha256=G_8rQ0VzuKIZHWs24O_WrfzpC5S27R1JsL-bFBR3SUQ,117730
117
+ execsql2-2.21.1.dist-info/METADATA,sha256=huk0LlRjMH6EmtqJgkKkpmAGon-Qhb6j9J51idFmwOc,22612
118
+ execsql2-2.21.1.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
119
+ execsql2-2.21.1.dist-info/entry_points.txt,sha256=sUOxkM-dN1eBGGpSpDLsAaE0yNXYQKWZAfxPOlMkQyk,90
120
+ execsql2-2.21.1.dist-info/licenses/LICENSE.txt,sha256=LBdhuxejF8_bLCHZ2kWfmDXpDGUu914Gbd6_3JjCRe0,676
121
+ execsql2-2.21.1.dist-info/licenses/NOTICE,sha256=McYzgxYav3U1OaVsY4Su1sfBrfmplpRdA9b6-gCDQCg,342
122
+ execsql2-2.21.1.dist-info/RECORD,,