sqlalchemy-iris 0.16.1b1__py3-none-any.whl → 0.16.2b1__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.
- sqlalchemy_iris/__init__.py +3 -2
- sqlalchemy_iris/base.py +5 -1
- sqlalchemy_iris/intersystems/__init__.py +167 -0
- sqlalchemy_iris/intersystems/cursor.py +17 -0
- sqlalchemy_iris/intersystems/dbapi.py +67 -0
- sqlalchemy_iris/requirements.py +1236 -16
- sqlalchemy_iris/types.py +32 -0
- {sqlalchemy_iris-0.16.1b1.dist-info → sqlalchemy_iris-0.16.2b1.dist-info}/METADATA +23 -1
- {sqlalchemy_iris-0.16.1b1.dist-info → sqlalchemy_iris-0.16.2b1.dist-info}/RECORD +13 -10
- {sqlalchemy_iris-0.16.1b1.dist-info → sqlalchemy_iris-0.16.2b1.dist-info}/entry_points.txt +1 -0
- {sqlalchemy_iris-0.16.1b1.dist-info → sqlalchemy_iris-0.16.2b1.dist-info}/LICENSE +0 -0
- {sqlalchemy_iris-0.16.1b1.dist-info → sqlalchemy_iris-0.16.2b1.dist-info}/WHEEL +0 -0
- {sqlalchemy_iris-0.16.1b1.dist-info → sqlalchemy_iris-0.16.2b1.dist-info}/top_level.txt +0 -0
sqlalchemy_iris/__init__.py
CHANGED
@@ -4,11 +4,11 @@ from . import base
|
|
4
4
|
from . import iris
|
5
5
|
|
6
6
|
try:
|
7
|
-
import alembic
|
7
|
+
import alembic # noqa
|
8
8
|
except ImportError:
|
9
9
|
pass
|
10
10
|
else:
|
11
|
-
from .alembic import IRISImpl
|
11
|
+
from .alembic import IRISImpl # noqa
|
12
12
|
|
13
13
|
from .base import BIGINT
|
14
14
|
from .base import BIT
|
@@ -32,6 +32,7 @@ base.dialect = dialect = iris.dialect
|
|
32
32
|
_registry.register("iris.iris", "sqlalchemy_iris.iris", "IRISDialect_iris")
|
33
33
|
_registry.register("iris.emb", "sqlalchemy_iris.embedded", "IRISDialect_emb")
|
34
34
|
_registry.register("iris.irisasync", "sqlalchemy_iris.irisasync", "IRISDialect_irisasync")
|
35
|
+
_registry.register("iris.intersystems", "sqlalchemy_iris.intersystems", "IRISDialect_intersystems")
|
35
36
|
|
36
37
|
__all__ = [
|
37
38
|
"BIGINT",
|
sqlalchemy_iris/base.py
CHANGED
@@ -900,6 +900,7 @@ class IRISDialect(default.DefaultDialect):
|
|
900
900
|
type_compiler = IRISTypeCompiler
|
901
901
|
execution_ctx_cls = IRISExecutionContext
|
902
902
|
|
903
|
+
update_returning = False
|
903
904
|
insert_returning = True
|
904
905
|
insert_executemany_returning = True
|
905
906
|
insert_executemany_returning_sort_by_parameter_order = True
|
@@ -1090,7 +1091,10 @@ There are no access to %Dictionary, may be required for some advanced features,
|
|
1090
1091
|
if query.endswith(";"):
|
1091
1092
|
query = query[:-1]
|
1092
1093
|
self._debug(query, params)
|
1093
|
-
|
1094
|
+
try:
|
1095
|
+
cursor.execute(query, params)
|
1096
|
+
except Exception as ex:
|
1097
|
+
raise ex
|
1094
1098
|
|
1095
1099
|
def do_executemany(self, cursor, query, params, context=None):
|
1096
1100
|
if query.endswith(";"):
|
@@ -0,0 +1,167 @@
|
|
1
|
+
import re
|
2
|
+
import pkg_resources
|
3
|
+
from ..base import IRISDialect
|
4
|
+
from sqlalchemy import text, util
|
5
|
+
from ..base import IRISExecutionContext
|
6
|
+
from . import dbapi
|
7
|
+
from .dbapi import connect, Cursor
|
8
|
+
from .cursor import InterSystemsCursorFetchStrategy
|
9
|
+
from .dbapi import IntegrityError, OperationalError, DatabaseError
|
10
|
+
|
11
|
+
|
12
|
+
def remap_exception(func):
|
13
|
+
def wrapper(cursor, *args, **kwargs):
|
14
|
+
attempt = 0
|
15
|
+
while attempt < 3:
|
16
|
+
attempt += 1
|
17
|
+
try:
|
18
|
+
cursor.sqlcode = 0
|
19
|
+
return func(cursor, *args, **kwargs)
|
20
|
+
except RuntimeError as ex:
|
21
|
+
# [SQLCODE: <-119>:...
|
22
|
+
message = ex.args[0]
|
23
|
+
if '<LIST ERROR>' in message:
|
24
|
+
# just random error happens in the driver, try again
|
25
|
+
continue
|
26
|
+
sqlcode = re.findall(r"^\[SQLCODE: <(-\d+)>:", message)
|
27
|
+
if not sqlcode:
|
28
|
+
raise Exception(message)
|
29
|
+
sqlcode = int(sqlcode[0])
|
30
|
+
if abs(sqlcode) in [108, 119, 121, 122]:
|
31
|
+
raise IntegrityError(message)
|
32
|
+
if abs(sqlcode) in [1, 12]:
|
33
|
+
raise OperationalError(message)
|
34
|
+
raise DatabaseError(message)
|
35
|
+
|
36
|
+
return wrapper
|
37
|
+
|
38
|
+
|
39
|
+
class InterSystemsExecutionContext(IRISExecutionContext):
|
40
|
+
cursor_fetch_strategy = InterSystemsCursorFetchStrategy()
|
41
|
+
|
42
|
+
def create_cursor(self):
|
43
|
+
cursor = self._dbapi_connection.cursor()
|
44
|
+
cursor.sqlcode = 0
|
45
|
+
return cursor
|
46
|
+
|
47
|
+
|
48
|
+
class IRISDialect_intersystems(IRISDialect):
|
49
|
+
driver = "intersystems"
|
50
|
+
|
51
|
+
execution_ctx_cls = InterSystemsExecutionContext
|
52
|
+
|
53
|
+
supports_statement_cache = True
|
54
|
+
|
55
|
+
supports_cte = False
|
56
|
+
|
57
|
+
supports_sane_rowcount = False
|
58
|
+
supports_sane_multi_rowcount = False
|
59
|
+
|
60
|
+
insert_returning = False
|
61
|
+
insert_executemany_returning = False
|
62
|
+
|
63
|
+
logfile = None
|
64
|
+
|
65
|
+
def __init__(self, logfile: str = None, **kwargs):
|
66
|
+
self.logfile = logfile
|
67
|
+
IRISDialect.__init__(self, **kwargs)
|
68
|
+
|
69
|
+
@classmethod
|
70
|
+
def import_dbapi(cls):
|
71
|
+
return dbapi
|
72
|
+
|
73
|
+
def connect(self, *cargs, **kwarg):
|
74
|
+
host = kwarg.get("hostname", "localhost")
|
75
|
+
port = kwarg.get("port", 1972)
|
76
|
+
namespace = kwarg.get("namespace", "USER")
|
77
|
+
username = kwarg.get("username", "_SYSTEM")
|
78
|
+
password = kwarg.get("password", "SYS")
|
79
|
+
timeout = kwarg.get("timeout", 10)
|
80
|
+
sharedmemory = kwarg.get("sharedmemory", False)
|
81
|
+
logfile = kwarg.get("logfile", self.logfile)
|
82
|
+
sslconfig = kwarg.get("sslconfig", False)
|
83
|
+
autoCommit = kwarg.get("autoCommit", False)
|
84
|
+
isolationLevel = kwarg.get("isolationLevel", 1)
|
85
|
+
return connect(
|
86
|
+
host,
|
87
|
+
port,
|
88
|
+
namespace,
|
89
|
+
username,
|
90
|
+
password,
|
91
|
+
timeout,
|
92
|
+
sharedmemory,
|
93
|
+
logfile,
|
94
|
+
sslconfig,
|
95
|
+
autoCommit,
|
96
|
+
isolationLevel,
|
97
|
+
)
|
98
|
+
|
99
|
+
def create_connect_args(self, url):
|
100
|
+
opts = {}
|
101
|
+
|
102
|
+
opts["application_name"] = "sqlalchemy"
|
103
|
+
opts["host"] = url.host
|
104
|
+
opts["port"] = int(url.port) if url.port else 1972
|
105
|
+
opts["namespace"] = url.database if url.database else "USER"
|
106
|
+
opts["username"] = url.username if url.username else ""
|
107
|
+
opts["password"] = url.password if url.password else ""
|
108
|
+
|
109
|
+
opts["autoCommit"] = False
|
110
|
+
|
111
|
+
if opts["host"] and "@" in opts["host"]:
|
112
|
+
_h = opts["host"].split("@")
|
113
|
+
opts["password"] += "@" + _h[0 : len(_h) - 1].join("@")
|
114
|
+
opts["host"] = _h[len(_h) - 1]
|
115
|
+
|
116
|
+
return ([], opts)
|
117
|
+
|
118
|
+
def _get_server_version_info(self, connection):
|
119
|
+
# get the wheel version from iris module
|
120
|
+
try:
|
121
|
+
return tuple(
|
122
|
+
map(
|
123
|
+
int,
|
124
|
+
pkg_resources.get_distribution(
|
125
|
+
"intersystems_irispython"
|
126
|
+
).version.split("."),
|
127
|
+
)
|
128
|
+
)
|
129
|
+
except: # noqa
|
130
|
+
return None
|
131
|
+
|
132
|
+
def _get_option(self, connection, option):
|
133
|
+
with connection.cursor() as cursor:
|
134
|
+
cursor.execute("SELECT %SYSTEM_SQL.Util_GetOption(?)", (option,))
|
135
|
+
row = cursor.fetchone()
|
136
|
+
if row:
|
137
|
+
return row[0]
|
138
|
+
return None
|
139
|
+
|
140
|
+
def set_isolation_level(self, connection, level_str):
|
141
|
+
if level_str == "AUTOCOMMIT":
|
142
|
+
connection.autocommit = True
|
143
|
+
else:
|
144
|
+
connection.autocommit = False
|
145
|
+
if level_str not in ["READ COMMITTED", "READ VERIFIED"]:
|
146
|
+
level_str = "READ UNCOMMITTED"
|
147
|
+
with connection.cursor() as cursor:
|
148
|
+
cursor.execute("SET TRANSACTION ISOLATION LEVEL " + level_str)
|
149
|
+
|
150
|
+
@remap_exception
|
151
|
+
def do_execute(self, cursor, query, params, context=None):
|
152
|
+
if query.endswith(";"):
|
153
|
+
query = query[:-1]
|
154
|
+
self._debug(query, params)
|
155
|
+
cursor.execute(query, params)
|
156
|
+
|
157
|
+
@remap_exception
|
158
|
+
def do_executemany(self, cursor, query, params, context=None):
|
159
|
+
if query.endswith(";"):
|
160
|
+
query = query[:-1]
|
161
|
+
self._debug(query, params, many=True)
|
162
|
+
if params and (len(params[0]) <= 1):
|
163
|
+
params = [param[0] if len(param) else None for param in params]
|
164
|
+
cursor.executemany(query, params)
|
165
|
+
|
166
|
+
|
167
|
+
dialect = IRISDialect_intersystems
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from typing import Any
|
2
|
+
from sqlalchemy import CursorResult
|
3
|
+
from sqlalchemy.engine.cursor import CursorFetchStrategy
|
4
|
+
from sqlalchemy.engine.interfaces import DBAPICursor
|
5
|
+
|
6
|
+
|
7
|
+
class InterSystemsCursorFetchStrategy(CursorFetchStrategy):
|
8
|
+
|
9
|
+
def fetchone(
|
10
|
+
self,
|
11
|
+
result: CursorResult[Any],
|
12
|
+
dbapi_cursor: DBAPICursor,
|
13
|
+
hard_close: bool = False,
|
14
|
+
) -> Any:
|
15
|
+
row = dbapi_cursor.fetchone()
|
16
|
+
return tuple(row) if row else None
|
17
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
try:
|
2
|
+
import iris
|
3
|
+
|
4
|
+
class Cursor(iris.irissdk.dbapiCursor):
|
5
|
+
pass
|
6
|
+
|
7
|
+
class DataRow(iris.irissdk.dbapiDataRow):
|
8
|
+
pass
|
9
|
+
|
10
|
+
except ImportError:
|
11
|
+
pass
|
12
|
+
|
13
|
+
|
14
|
+
def connect(*args, **kwargs):
|
15
|
+
return iris.connect(*args, **kwargs)
|
16
|
+
|
17
|
+
|
18
|
+
# globals
|
19
|
+
apilevel = "2.0"
|
20
|
+
threadsafety = 0
|
21
|
+
paramstyle = "qmark"
|
22
|
+
|
23
|
+
Binary = bytes
|
24
|
+
STRING = str
|
25
|
+
BINARY = bytes
|
26
|
+
NUMBER = float
|
27
|
+
ROWID = str
|
28
|
+
|
29
|
+
|
30
|
+
class Error(Exception):
|
31
|
+
pass
|
32
|
+
|
33
|
+
|
34
|
+
class Warning(Exception):
|
35
|
+
pass
|
36
|
+
|
37
|
+
|
38
|
+
class InterfaceError(Error):
|
39
|
+
pass
|
40
|
+
|
41
|
+
|
42
|
+
class DatabaseError(Error):
|
43
|
+
pass
|
44
|
+
|
45
|
+
|
46
|
+
class InternalError(DatabaseError):
|
47
|
+
pass
|
48
|
+
|
49
|
+
|
50
|
+
class OperationalError(DatabaseError):
|
51
|
+
pass
|
52
|
+
|
53
|
+
|
54
|
+
class ProgrammingError(DatabaseError):
|
55
|
+
pass
|
56
|
+
|
57
|
+
|
58
|
+
class IntegrityError(DatabaseError):
|
59
|
+
pass
|
60
|
+
|
61
|
+
|
62
|
+
class DataError(DatabaseError):
|
63
|
+
pass
|
64
|
+
|
65
|
+
|
66
|
+
class NotSupportedError(DatabaseError):
|
67
|
+
pass
|