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.
- execsql/db/factory.py +1 -1
- execsql/db/postgres.py +28 -18
- {execsql2-2.20.0.dist-info → execsql2-2.21.1.dist-info}/METADATA +7 -7
- {execsql2-2.20.0.dist-info → execsql2-2.21.1.dist-info}/RECORD +22 -22
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/README.md +0 -0
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/config_settings.sqlite +0 -0
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/make_config_db.sql +0 -0
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/md_compare.sql +0 -0
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/md_glossary.sql +0 -0
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/md_upsert.sql +0 -0
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/pg_compare.sql +0 -0
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/pg_glossary.sql +0 -0
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/pg_upsert.sql +0 -0
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/script_template.sql +0 -0
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/ss_compare.sql +0 -0
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/ss_glossary.sql +0 -0
- {execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/ss_upsert.sql +0 -0
- {execsql2-2.20.0.dist-info → execsql2-2.21.1.dist-info}/WHEEL +0 -0
- {execsql2-2.20.0.dist-info → execsql2-2.21.1.dist-info}/entry_points.txt +0 -0
- {execsql2-2.20.0.dist-info → execsql2-2.21.1.dist-info}/licenses/LICENSE.txt +0 -0
- {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
|
|
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 ``
|
|
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
|
|
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
|
|
44
|
+
import psycopg # noqa: F401
|
|
45
45
|
except Exception:
|
|
46
46
|
fatal_error(
|
|
47
|
-
"The
|
|
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
|
|
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
|
|
88
|
+
return psycopg.connect(
|
|
82
89
|
host=str(db.server_name),
|
|
83
|
-
|
|
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
|
|
98
|
+
return psycopg.connect(
|
|
91
99
|
host=str(db.server_name),
|
|
92
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
55
|
+
Requires-Dist: pg-upsert>=1.23.0; extra == 'all'
|
|
56
56
|
Requires-Dist: polars>=1.0; extra == 'all'
|
|
57
|
-
Requires-Dist:
|
|
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:
|
|
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:
|
|
127
|
+
Requires-Dist: psycopg[binary]<4,>=3.1; extra == 'postgres'
|
|
128
128
|
Provides-Extra: upsert
|
|
129
|
-
Requires-Dist: pg-upsert>=1.
|
|
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.
|
|
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=
|
|
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=
|
|
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.
|
|
104
|
-
execsql2-2.
|
|
105
|
-
execsql2-2.
|
|
106
|
-
execsql2-2.
|
|
107
|
-
execsql2-2.
|
|
108
|
-
execsql2-2.
|
|
109
|
-
execsql2-2.
|
|
110
|
-
execsql2-2.
|
|
111
|
-
execsql2-2.
|
|
112
|
-
execsql2-2.
|
|
113
|
-
execsql2-2.
|
|
114
|
-
execsql2-2.
|
|
115
|
-
execsql2-2.
|
|
116
|
-
execsql2-2.
|
|
117
|
-
execsql2-2.
|
|
118
|
-
execsql2-2.
|
|
119
|
-
execsql2-2.
|
|
120
|
-
execsql2-2.
|
|
121
|
-
execsql2-2.
|
|
122
|
-
execsql2-2.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
{execsql2-2.20.0.data → execsql2-2.21.1.data}/data/execsql2_extras/example_config_prompt.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|