sqlalchemy-seerdb 0.2.0__tar.gz → 0.2.2__tar.gz
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.
- {sqlalchemy_seerdb-0.2.0/sqlalchemy_seerdb.egg-info → sqlalchemy_seerdb-0.2.2}/PKG-INFO +21 -4
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/README.md +20 -3
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/pyproject.toml +1 -1
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/sqlalchemy_seerdb/__init__.py +1 -1
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/sqlalchemy_seerdb/seerdb.py +55 -3
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2/sqlalchemy_seerdb.egg-info}/PKG-INFO +21 -4
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/sqlalchemy_seerdb.egg-info/SOURCES.txt +1 -0
- sqlalchemy_seerdb-0.2.2/test/test_boolean.py +32 -0
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/LICENSES/MIT.txt +0 -0
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/setup.cfg +0 -0
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/sqlalchemy_seerdb/provision.py +0 -0
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/sqlalchemy_seerdb/requirements.py +0 -0
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/sqlalchemy_seerdb.egg-info/dependency_links.txt +0 -0
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/sqlalchemy_seerdb.egg-info/entry_points.txt +0 -0
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/sqlalchemy_seerdb.egg-info/requires.txt +0 -0
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/sqlalchemy_seerdb.egg-info/top_level.txt +0 -0
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/test/test_connect_args.py +0 -0
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/test/test_suite.py +0 -0
- {sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/test/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlalchemy-seerdb
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: SQLAlchemy dialect for the seerdb driver
|
|
5
5
|
Author-email: Peter Lemenkov <lemenkov@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -93,16 +93,33 @@ builds tables there.
|
|
|
93
93
|
|
|
94
94
|
The test account cannot create it (`ORA-01031`), so run this as a DBA once:
|
|
95
95
|
|
|
96
|
+
```
|
|
97
|
+
python test/prepare_test_schema.py --host localhost --service XE \
|
|
98
|
+
--dba-password <SYSTEM password> --test-user <your test user>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
It is safe to rerun: each statement is attempted on its own and one that has
|
|
102
|
+
already been applied is reported and skipped. What it runs, for a DBA who would
|
|
103
|
+
rather type it:
|
|
104
|
+
|
|
96
105
|
```sql
|
|
106
|
+
-- the test account's own tables must live in USERS: the Oracle dialect hides
|
|
107
|
+
-- SYSTEM-tablespace tables from reflection, and the suite then never empties
|
|
108
|
+
-- them between tests (rows "survive", ORA-00001)
|
|
109
|
+
ALTER USER <your test user> DEFAULT TABLESPACE USERS;
|
|
110
|
+
ALTER USER <your test user> QUOTA UNLIMITED ON USERS;
|
|
111
|
+
|
|
97
112
|
CREATE USER test_schema IDENTIFIED BY test_schema;
|
|
98
113
|
GRANT CREATE SESSION TO test_schema;
|
|
99
|
-
ALTER USER test_schema
|
|
114
|
+
ALTER USER test_schema DEFAULT TABLESPACE USERS;
|
|
115
|
+
GRANT UNLIMITED TABLESPACE TO test_schema;
|
|
100
116
|
|
|
101
|
-
-- so the test account can build and drop the fixtures inside that schema
|
|
117
|
+
-- so the test account can build and drop the fixtures inside that schema;
|
|
118
|
+
-- SELECT ANY SEQUENCE keeps sequences in test_schema visible to reflection
|
|
102
119
|
GRANT CREATE ANY TABLE, DROP ANY TABLE, SELECT ANY TABLE, INSERT ANY TABLE,
|
|
103
120
|
UPDATE ANY TABLE, DELETE ANY TABLE, CREATE ANY INDEX, DROP ANY INDEX,
|
|
104
121
|
CREATE ANY VIEW, DROP ANY VIEW, CREATE ANY SEQUENCE, DROP ANY SEQUENCE,
|
|
105
|
-
COMMENT ANY TABLE, ANALYZE ANY TO <your test user>;
|
|
122
|
+
SELECT ANY SEQUENCE, COMMENT ANY TABLE, ANALYZE ANY TO <your test user>;
|
|
106
123
|
```
|
|
107
124
|
|
|
108
125
|
Those `ANY` privileges are broad. They are fine on a throwaway test instance —
|
|
@@ -71,16 +71,33 @@ builds tables there.
|
|
|
71
71
|
|
|
72
72
|
The test account cannot create it (`ORA-01031`), so run this as a DBA once:
|
|
73
73
|
|
|
74
|
+
```
|
|
75
|
+
python test/prepare_test_schema.py --host localhost --service XE \
|
|
76
|
+
--dba-password <SYSTEM password> --test-user <your test user>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
It is safe to rerun: each statement is attempted on its own and one that has
|
|
80
|
+
already been applied is reported and skipped. What it runs, for a DBA who would
|
|
81
|
+
rather type it:
|
|
82
|
+
|
|
74
83
|
```sql
|
|
84
|
+
-- the test account's own tables must live in USERS: the Oracle dialect hides
|
|
85
|
+
-- SYSTEM-tablespace tables from reflection, and the suite then never empties
|
|
86
|
+
-- them between tests (rows "survive", ORA-00001)
|
|
87
|
+
ALTER USER <your test user> DEFAULT TABLESPACE USERS;
|
|
88
|
+
ALTER USER <your test user> QUOTA UNLIMITED ON USERS;
|
|
89
|
+
|
|
75
90
|
CREATE USER test_schema IDENTIFIED BY test_schema;
|
|
76
91
|
GRANT CREATE SESSION TO test_schema;
|
|
77
|
-
ALTER USER test_schema
|
|
92
|
+
ALTER USER test_schema DEFAULT TABLESPACE USERS;
|
|
93
|
+
GRANT UNLIMITED TABLESPACE TO test_schema;
|
|
78
94
|
|
|
79
|
-
-- so the test account can build and drop the fixtures inside that schema
|
|
95
|
+
-- so the test account can build and drop the fixtures inside that schema;
|
|
96
|
+
-- SELECT ANY SEQUENCE keeps sequences in test_schema visible to reflection
|
|
80
97
|
GRANT CREATE ANY TABLE, DROP ANY TABLE, SELECT ANY TABLE, INSERT ANY TABLE,
|
|
81
98
|
UPDATE ANY TABLE, DELETE ANY TABLE, CREATE ANY INDEX, DROP ANY INDEX,
|
|
82
99
|
CREATE ANY VIEW, DROP ANY VIEW, CREATE ANY SEQUENCE, DROP ANY SEQUENCE,
|
|
83
|
-
COMMENT ANY TABLE, ANALYZE ANY TO <your test user>;
|
|
100
|
+
SELECT ANY SEQUENCE, COMMENT ANY TABLE, ANALYZE ANY TO <your test user>;
|
|
84
101
|
```
|
|
85
102
|
|
|
86
103
|
Those `ANY` privileges are broad. They are fine on a throwaway test instance —
|
|
@@ -32,7 +32,7 @@ from sqlalchemy.dialects.oracle.base import (
|
|
|
32
32
|
OracleDialect,
|
|
33
33
|
OracleExecutionContext,
|
|
34
34
|
)
|
|
35
|
-
from sqlalchemy.dialects.oracle.types import _OracleDate
|
|
35
|
+
from sqlalchemy.dialects.oracle.types import _OracleBoolean, _OracleDate
|
|
36
36
|
from sqlalchemy.engine import cursor as _cursor
|
|
37
37
|
from sqlalchemy.engine import interfaces
|
|
38
38
|
|
|
@@ -72,6 +72,25 @@ class _SeerdbDate(_OracleDate):
|
|
|
72
72
|
return process
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
class _SeerdbBoolean(_OracleBoolean):
|
|
76
|
+
"""A `Boolean` column read back as a `bool`.
|
|
77
|
+
|
|
78
|
+
Before 23ai there is no BOOLEAN type, so the column is a NUMBER and the
|
|
79
|
+
driver reads 1 or 0 back as a number. From SQLAlchemy 2.1 the generic
|
|
80
|
+
dialect leaves the conversion to an output type handler that only the
|
|
81
|
+
drivers it ships with install, so here it is taken as it is for `Date`. A
|
|
82
|
+
native BOOLEAN already arrives as a `bool` and passes through.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def result_processor(self, dialect, coltype):
|
|
86
|
+
def process(value):
|
|
87
|
+
if value is None or isinstance(value, bool):
|
|
88
|
+
return value
|
|
89
|
+
return bool(value)
|
|
90
|
+
|
|
91
|
+
return process
|
|
92
|
+
|
|
93
|
+
|
|
75
94
|
class SeerdbExecutionContext(OracleExecutionContext):
|
|
76
95
|
"""Turns this backend's RETURNING into the rows SQLAlchemy expects.
|
|
77
96
|
|
|
@@ -238,6 +257,23 @@ class SeerdbCompiler(OracleCompiler):
|
|
|
238
257
|
return super().bindparam_string(name, **kw)
|
|
239
258
|
|
|
240
259
|
|
|
260
|
+
def _driver_has_fetch_lobs() -> bool:
|
|
261
|
+
"""Whether the installed driver understands ``fetch_lobs``.
|
|
262
|
+
|
|
263
|
+
It arrived in seerdb 3.0 together with the LOB object; passing it to an
|
|
264
|
+
older driver is a TypeError, and this dialect supports both."""
|
|
265
|
+
import inspect
|
|
266
|
+
|
|
267
|
+
try:
|
|
268
|
+
params = inspect.signature(seerdb.OracleConnect.__init__).parameters
|
|
269
|
+
except (AttributeError, TypeError, ValueError):
|
|
270
|
+
return False
|
|
271
|
+
return 'fetch_lobs' in params
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
_DRIVER_HAS_FETCH_LOBS = _driver_has_fetch_lobs()
|
|
275
|
+
|
|
276
|
+
|
|
241
277
|
class SeerdbDialect(OracleDialect):
|
|
242
278
|
"""SQLAlchemy dialect driving the seerdb DBAPI."""
|
|
243
279
|
|
|
@@ -271,8 +307,13 @@ class SeerdbDialect(OracleDialect):
|
|
|
271
307
|
# no processor either way, so a Float column came back holding a Decimal.
|
|
272
308
|
supports_native_decimal = True
|
|
273
309
|
|
|
274
|
-
# As the base dialect's, plus the date narrowing
|
|
275
|
-
|
|
310
|
+
# As the base dialect's, plus the date narrowing and the boolean
|
|
311
|
+
# conversion above.
|
|
312
|
+
colspecs: ClassVar[dict] = {
|
|
313
|
+
**_oracle_base.colspecs,
|
|
314
|
+
sqltypes.Date: _SeerdbDate,
|
|
315
|
+
sqltypes.Boolean: _SeerdbBoolean,
|
|
316
|
+
}
|
|
276
317
|
|
|
277
318
|
# No SQL is generated differently from the base dialect, so cached
|
|
278
319
|
# statements stay valid.
|
|
@@ -347,6 +388,17 @@ class SeerdbDialect(OracleDialect):
|
|
|
347
388
|
# DBAPI contract SQLAlchemy builds on is autocommit off, so that is the
|
|
348
389
|
# default here; ``?autocommit=true`` in the URL still turns it on.
|
|
349
390
|
options['autocommit'] = False
|
|
391
|
+
# SQLAlchemy's type machinery expects a CLOB / BLOB column to arrive as
|
|
392
|
+
# str / bytes -- its Text and LargeBinary result processors, and every
|
|
393
|
+
# comparison the compliance suite makes, are written against values.
|
|
394
|
+
# seerdb 3.0 returns a LOB OBJECT by default instead (matching
|
|
395
|
+
# python-oracledb), so ask for the values this dialect is built on.
|
|
396
|
+
#
|
|
397
|
+
# Feature-detected rather than pinned: the same dialect still has to
|
|
398
|
+
# work against a driver that predates the option, and its floor cannot
|
|
399
|
+
# be raised to a release that is not published yet.
|
|
400
|
+
if _DRIVER_HAS_FETCH_LOBS:
|
|
401
|
+
options['fetch_lobs'] = False
|
|
350
402
|
for key, value in url.query.items():
|
|
351
403
|
# A repeated key arrives as a tuple; the driver takes one value.
|
|
352
404
|
options[key] = _coerce(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlalchemy-seerdb
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: SQLAlchemy dialect for the seerdb driver
|
|
5
5
|
Author-email: Peter Lemenkov <lemenkov@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -93,16 +93,33 @@ builds tables there.
|
|
|
93
93
|
|
|
94
94
|
The test account cannot create it (`ORA-01031`), so run this as a DBA once:
|
|
95
95
|
|
|
96
|
+
```
|
|
97
|
+
python test/prepare_test_schema.py --host localhost --service XE \
|
|
98
|
+
--dba-password <SYSTEM password> --test-user <your test user>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
It is safe to rerun: each statement is attempted on its own and one that has
|
|
102
|
+
already been applied is reported and skipped. What it runs, for a DBA who would
|
|
103
|
+
rather type it:
|
|
104
|
+
|
|
96
105
|
```sql
|
|
106
|
+
-- the test account's own tables must live in USERS: the Oracle dialect hides
|
|
107
|
+
-- SYSTEM-tablespace tables from reflection, and the suite then never empties
|
|
108
|
+
-- them between tests (rows "survive", ORA-00001)
|
|
109
|
+
ALTER USER <your test user> DEFAULT TABLESPACE USERS;
|
|
110
|
+
ALTER USER <your test user> QUOTA UNLIMITED ON USERS;
|
|
111
|
+
|
|
97
112
|
CREATE USER test_schema IDENTIFIED BY test_schema;
|
|
98
113
|
GRANT CREATE SESSION TO test_schema;
|
|
99
|
-
ALTER USER test_schema
|
|
114
|
+
ALTER USER test_schema DEFAULT TABLESPACE USERS;
|
|
115
|
+
GRANT UNLIMITED TABLESPACE TO test_schema;
|
|
100
116
|
|
|
101
|
-
-- so the test account can build and drop the fixtures inside that schema
|
|
117
|
+
-- so the test account can build and drop the fixtures inside that schema;
|
|
118
|
+
-- SELECT ANY SEQUENCE keeps sequences in test_schema visible to reflection
|
|
102
119
|
GRANT CREATE ANY TABLE, DROP ANY TABLE, SELECT ANY TABLE, INSERT ANY TABLE,
|
|
103
120
|
UPDATE ANY TABLE, DELETE ANY TABLE, CREATE ANY INDEX, DROP ANY INDEX,
|
|
104
121
|
CREATE ANY VIEW, DROP ANY VIEW, CREATE ANY SEQUENCE, DROP ANY SEQUENCE,
|
|
105
|
-
COMMENT ANY TABLE, ANALYZE ANY TO <your test user>;
|
|
122
|
+
SELECT ANY SEQUENCE, COMMENT ANY TABLE, ANALYZE ANY TO <your test user>;
|
|
106
123
|
```
|
|
107
124
|
|
|
108
125
|
Those `ANY` privileges are broad. They are fine on a throwaway test instance —
|
|
@@ -11,6 +11,7 @@ sqlalchemy_seerdb.egg-info/dependency_links.txt
|
|
|
11
11
|
sqlalchemy_seerdb.egg-info/entry_points.txt
|
|
12
12
|
sqlalchemy_seerdb.egg-info/requires.txt
|
|
13
13
|
sqlalchemy_seerdb.egg-info/top_level.txt
|
|
14
|
+
test/test_boolean.py
|
|
14
15
|
test/test_connect_args.py
|
|
15
16
|
test/test_suite.py
|
|
16
17
|
test/test_version.py
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Peter Lemenkov <lemenkov@gmail.com>
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""A Boolean column comes back as a bool whatever the driver returns."""
|
|
5
|
+
|
|
6
|
+
import unittest
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import types as sqltypes
|
|
9
|
+
|
|
10
|
+
from sqlalchemy_seerdb.seerdb import SeerdbDialect, _SeerdbBoolean
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestBoolean(unittest.TestCase):
|
|
14
|
+
def setUp(self):
|
|
15
|
+
self.process = _SeerdbBoolean().result_processor(SeerdbDialect(), None)
|
|
16
|
+
|
|
17
|
+
def test_a_number_becomes_a_bool(self):
|
|
18
|
+
# Before 23ai the column is a NUMBER, and the driver reads 1 / 0.
|
|
19
|
+
self.assertIs(self.process(1), True)
|
|
20
|
+
self.assertIs(self.process(0), False)
|
|
21
|
+
|
|
22
|
+
def test_a_native_bool_and_a_null_pass_through(self):
|
|
23
|
+
self.assertIs(self.process(True), True)
|
|
24
|
+
self.assertIs(self.process(False), False)
|
|
25
|
+
self.assertIsNone(self.process(None))
|
|
26
|
+
|
|
27
|
+
def test_the_dialect_uses_it_for_boolean(self):
|
|
28
|
+
self.assertIs(SeerdbDialect.colspecs[sqltypes.Boolean], _SeerdbBoolean)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == '__main__':
|
|
32
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/sqlalchemy_seerdb.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/sqlalchemy_seerdb.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{sqlalchemy_seerdb-0.2.0 → sqlalchemy_seerdb-0.2.2}/sqlalchemy_seerdb.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|