databend-sqlalchemy 0.3.2__tar.gz → 0.4.0__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.
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/PKG-INFO +4 -4
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/README.rst +2 -2
- databend_sqlalchemy-0.4.0/databend_sqlalchemy/__init__.py +5 -0
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy/connector.py +44 -52
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy/databend_dialect.py +7 -7
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy/test.py +3 -1
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy.egg-info/PKG-INFO +4 -4
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy.egg-info/SOURCES.txt +0 -2
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy.egg-info/requires.txt +1 -1
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy.egg-info/top_level.txt +0 -1
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/pyproject.toml +2 -5
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/setup.cfg +1 -1
- databend_sqlalchemy-0.3.2/databend_sqlalchemy/__init__.py +0 -7
- databend_sqlalchemy-0.3.2/databend_sqlalchemy/entry_points.py +0 -40
- databend_sqlalchemy-0.3.2/tests/__init__.py +0 -0
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/LICENSE +0 -0
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/cc_superset/__init__.py +0 -0
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/cc_superset/engine.py +0 -0
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy/errors.py +0 -0
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy.egg-info/dependency_links.txt +0 -0
- {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy.egg-info/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: databend_sqlalchemy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Sqlalchemy adapter for Databend
|
|
5
5
|
Home-page: https://github.com/datafuselabs/databend-sqlalchemy
|
|
6
6
|
Author: Databend Cloud
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
19
19
|
Requires-Python: >=3.7
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: databend_py>=0.
|
|
22
|
+
Requires-Dist: databend_py>=0.5.0
|
|
23
23
|
Requires-Dist: sqlalchemy<2.0,>1.3.21
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: devtools==0.7.0; extra == "dev"
|
|
@@ -51,14 +51,14 @@ The DSN format is similar to that of regular Postgres::
|
|
|
51
51
|
from sqlalchemy import create_engine, text
|
|
52
52
|
from sqlalchemy.engine.base import Connection, Engine
|
|
53
53
|
engine = create_engine(
|
|
54
|
-
f"databend://{username}:{password}@{host_port_name}/{database_name}?
|
|
54
|
+
f"databend://{username}:{password}@{host_port_name}/{database_name}?sslmode=disable"
|
|
55
55
|
)
|
|
56
56
|
connection = engine.connect()
|
|
57
57
|
result = connection.execute(text("SELECT 1"))
|
|
58
58
|
assert len(result.fetchall()) == 1
|
|
59
59
|
|
|
60
60
|
import connector
|
|
61
|
-
cursor = connector.connect('
|
|
61
|
+
cursor = connector.connect('databend://root:@localhost:8000?sslmode=disable').cursor()
|
|
62
62
|
cursor.execute('SELECT * FROM test')
|
|
63
63
|
# print(cursor.fetchone())
|
|
64
64
|
# print(cursor.fetchall())
|
|
@@ -18,14 +18,14 @@ The DSN format is similar to that of regular Postgres::
|
|
|
18
18
|
from sqlalchemy import create_engine, text
|
|
19
19
|
from sqlalchemy.engine.base import Connection, Engine
|
|
20
20
|
engine = create_engine(
|
|
21
|
-
f"databend://{username}:{password}@{host_port_name}/{database_name}?
|
|
21
|
+
f"databend://{username}:{password}@{host_port_name}/{database_name}?sslmode=disable"
|
|
22
22
|
)
|
|
23
23
|
connection = engine.connect()
|
|
24
24
|
result = connection.execute(text("SELECT 1"))
|
|
25
25
|
assert len(result.fetchall()) == 1
|
|
26
26
|
|
|
27
27
|
import connector
|
|
28
|
-
cursor = connector.connect('
|
|
28
|
+
cursor = connector.connect('databend://root:@localhost:8000?sslmode=disable').cursor()
|
|
29
29
|
cursor.execute('SELECT * FROM test')
|
|
30
30
|
# print(cursor.fetchone())
|
|
31
31
|
# print(cursor.fetchall())
|
|
@@ -4,14 +4,12 @@
|
|
|
4
4
|
#
|
|
5
5
|
# Many docstrings in this file are based on the PEP, which is in the public domain.
|
|
6
6
|
|
|
7
|
-
from __future__ import absolute_import
|
|
8
|
-
from __future__ import unicode_literals
|
|
9
7
|
import re
|
|
10
8
|
import uuid
|
|
11
9
|
from datetime import datetime
|
|
12
10
|
from databend_sqlalchemy.errors import ServerException, NotSupportedError
|
|
13
11
|
|
|
14
|
-
from databend_py import
|
|
12
|
+
from databend_py import BlockingDatabendClient
|
|
15
13
|
|
|
16
14
|
# PEP 249 module globals
|
|
17
15
|
apilevel = "2.0"
|
|
@@ -96,24 +94,22 @@ def connect(*args, **kwargs):
|
|
|
96
94
|
return Connection(*args, **kwargs)
|
|
97
95
|
|
|
98
96
|
|
|
99
|
-
class Connection
|
|
97
|
+
class Connection:
|
|
100
98
|
"""
|
|
101
99
|
These objects are small stateless factories for cursors, which do all the real work.
|
|
102
100
|
"""
|
|
103
101
|
|
|
104
|
-
def __init__(self,
|
|
105
|
-
|
|
106
|
-
self.db_url = db_url
|
|
107
|
-
self.client = Client.from_url(db_url)
|
|
102
|
+
def __init__(self, dsn="databend://root:@localhost:8000/?sslmode=disable"):
|
|
103
|
+
self.client = BlockingDatabendClient(dsn)
|
|
108
104
|
|
|
109
105
|
def close(self):
|
|
110
|
-
|
|
106
|
+
pass
|
|
111
107
|
|
|
112
108
|
def commit(self):
|
|
113
109
|
pass
|
|
114
110
|
|
|
115
111
|
def cursor(self):
|
|
116
|
-
return Cursor(self)
|
|
112
|
+
return Cursor(self.client.get_conn())
|
|
117
113
|
|
|
118
114
|
def rollback(self):
|
|
119
115
|
raise NotSupportedError("Transactions are not supported") # pragma: no cover
|
|
@@ -131,8 +127,8 @@ class Cursor(object):
|
|
|
131
127
|
_STATE_RUNNING = "Running"
|
|
132
128
|
_STATE_SUCCEEDED = "Succeeded"
|
|
133
129
|
|
|
134
|
-
def __init__(self,
|
|
135
|
-
self._db =
|
|
130
|
+
def __init__(self, conn):
|
|
131
|
+
self._db = conn
|
|
136
132
|
self._reset_state()
|
|
137
133
|
|
|
138
134
|
def _reset_state(self):
|
|
@@ -141,7 +137,7 @@ class Cursor(object):
|
|
|
141
137
|
self._rownumber = 0
|
|
142
138
|
# Internal helper state
|
|
143
139
|
self._state = self._STATE_NONE
|
|
144
|
-
self.
|
|
140
|
+
self._rows = None
|
|
145
141
|
self._columns = None
|
|
146
142
|
|
|
147
143
|
@property
|
|
@@ -183,10 +179,16 @@ class Cursor(object):
|
|
|
183
179
|
self._uuid = uuid.uuid1()
|
|
184
180
|
|
|
185
181
|
if is_response:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
182
|
+
rows = self._db.query_iter(operation)
|
|
183
|
+
schema = rows.schema()
|
|
184
|
+
columns = []
|
|
185
|
+
for field in schema.fields():
|
|
186
|
+
columns.append((field.name, field.data_type))
|
|
187
|
+
if self._state != self._STATE_RUNNING:
|
|
188
|
+
raise Exception("Should be running if processing response")
|
|
189
|
+
self._rows = rows
|
|
190
|
+
self._columns = columns
|
|
191
|
+
self._state = self._STATE_SUCCEEDED
|
|
190
192
|
|
|
191
193
|
def executemany(self, operation, seq_of_parameters):
|
|
192
194
|
"""Prepare a database operation (query or command) and then execute it against all parameter
|
|
@@ -221,11 +223,15 @@ class Cursor(object):
|
|
|
221
223
|
no more data is available."""
|
|
222
224
|
if self._state == self._STATE_NONE:
|
|
223
225
|
raise Exception("No query yet")
|
|
224
|
-
if not self.
|
|
225
|
-
|
|
226
|
+
if not self._rows:
|
|
227
|
+
raise Exception("No rows yet")
|
|
226
228
|
else:
|
|
227
229
|
self._rownumber += 1
|
|
228
|
-
|
|
230
|
+
try:
|
|
231
|
+
row = self._rows.__next__()
|
|
232
|
+
except StopIteration:
|
|
233
|
+
return None
|
|
234
|
+
return row.values()
|
|
229
235
|
|
|
230
236
|
def fetchmany(self, size=None):
|
|
231
237
|
"""Fetch the next set of rows of a query result, returning a sequence of sequences (e.g. a
|
|
@@ -242,15 +248,14 @@ class Cursor(object):
|
|
|
242
248
|
if size is None:
|
|
243
249
|
size = 1
|
|
244
250
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
return result
|
|
251
|
+
data = []
|
|
252
|
+
if self._rows:
|
|
253
|
+
for row in self._rows:
|
|
254
|
+
self._rownumber += 1
|
|
255
|
+
data.append(row.values())
|
|
256
|
+
if len(data) == size:
|
|
257
|
+
break
|
|
258
|
+
return data
|
|
254
259
|
|
|
255
260
|
def fetchall(self):
|
|
256
261
|
"""Fetch all (remaining) rows of a query result, returning them as a sequence of sequences
|
|
@@ -259,23 +264,19 @@ class Cursor(object):
|
|
|
259
264
|
if self._state == self._STATE_NONE:
|
|
260
265
|
raise Exception("No query yet")
|
|
261
266
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
267
|
+
data = []
|
|
268
|
+
if self._rows:
|
|
269
|
+
for row in self._rows:
|
|
270
|
+
self._rownumber += 1
|
|
271
|
+
data.append(row.values())
|
|
272
|
+
return data
|
|
268
273
|
|
|
269
274
|
def __next__(self):
|
|
270
275
|
"""Return the next row from the currently executing SQL statement using the same semantics
|
|
271
276
|
as :py:meth:`fetchone`. A ``StopIteration`` exception is raised when the result set is
|
|
272
277
|
exhausted.
|
|
273
278
|
"""
|
|
274
|
-
|
|
275
|
-
if one is None:
|
|
276
|
-
raise StopIteration
|
|
277
|
-
else:
|
|
278
|
-
return one
|
|
279
|
+
return self.fetchone()
|
|
279
280
|
|
|
280
281
|
next = __next__
|
|
281
282
|
|
|
@@ -287,23 +288,14 @@ class Cursor(object):
|
|
|
287
288
|
if self._state == self._STATE_NONE:
|
|
288
289
|
raise ServerException("No query yet")
|
|
289
290
|
if self._uuid is None:
|
|
290
|
-
|
|
291
|
+
if self._state != self._STATE_RUNNING:
|
|
292
|
+
raise ServerException("Query should be running")
|
|
291
293
|
return
|
|
292
294
|
# Replace current running query to cancel it
|
|
293
295
|
self._db.execute("SELECT 1")
|
|
294
296
|
self._state = self._STATE_SUCCEEDED
|
|
295
297
|
self._uuid = None
|
|
296
|
-
self.
|
|
298
|
+
self._rows = None
|
|
297
299
|
|
|
298
300
|
def poll(self):
|
|
299
301
|
pass
|
|
300
|
-
|
|
301
|
-
def _process_response(self, column_types, response):
|
|
302
|
-
"""Update the internal state with the data from the response"""
|
|
303
|
-
assert (
|
|
304
|
-
self._state == self._STATE_RUNNING
|
|
305
|
-
), "Should be running if processing response"
|
|
306
|
-
|
|
307
|
-
self._data = response
|
|
308
|
-
self._columns = column_types
|
|
309
|
-
self._state = self._STATE_SUCCEEDED
|
{databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy/databend_dialect.py
RENAMED
|
@@ -158,7 +158,7 @@ class DatabendCompiler(PGCompiler):
|
|
|
158
158
|
)
|
|
159
159
|
|
|
160
160
|
def visit_column(
|
|
161
|
-
|
|
161
|
+
self, column, add_to_result_map=None, include_table=True, **kwargs
|
|
162
162
|
):
|
|
163
163
|
# Columns prefixed with table name are not supported
|
|
164
164
|
return super(DatabendCompiler, self).visit_column(
|
|
@@ -233,7 +233,7 @@ class DatabendDialect(default.DefaultDialect):
|
|
|
233
233
|
_backslash_escapes = True
|
|
234
234
|
|
|
235
235
|
def __init__(
|
|
236
|
-
|
|
236
|
+
self, context: Optional[ExecutionContext] = None, *args: Any, **kwargs: Any
|
|
237
237
|
):
|
|
238
238
|
super(DatabendDialect, self).__init__(*args, **kwargs)
|
|
239
239
|
self.context: Union[ExecutionContext, Dict] = context or {}
|
|
@@ -242,7 +242,7 @@ class DatabendDialect(default.DefaultDialect):
|
|
|
242
242
|
def dbapi(cls):
|
|
243
243
|
try:
|
|
244
244
|
import databend_sqlalchemy.connector as connector
|
|
245
|
-
except:
|
|
245
|
+
except Exception:
|
|
246
246
|
import connector
|
|
247
247
|
return connector
|
|
248
248
|
|
|
@@ -256,11 +256,11 @@ class DatabendDialect(default.DefaultDialect):
|
|
|
256
256
|
def create_connect_args(self, url):
|
|
257
257
|
parameters = dict(url.query)
|
|
258
258
|
kwargs = {
|
|
259
|
-
"
|
|
260
|
-
|
|
259
|
+
"dsn": "databend://%s:%s@%s:%d/%s"
|
|
260
|
+
% (url.username, url.password, url.host, url.port or 8000, url.database),
|
|
261
261
|
}
|
|
262
262
|
for k, v in parameters.items():
|
|
263
|
-
kwargs["
|
|
263
|
+
kwargs["dsn"] = kwargs["dsn"] + "?" + k + "=" + v
|
|
264
264
|
|
|
265
265
|
return ([], kwargs)
|
|
266
266
|
|
|
@@ -318,7 +318,7 @@ class DatabendDialect(default.DefaultDialect):
|
|
|
318
318
|
|
|
319
319
|
def extract_nullable_string(self, target):
|
|
320
320
|
if "Nullable" in target:
|
|
321
|
-
match = re.match(r
|
|
321
|
+
match = re.match(r"Nullable\(([^)]+)\)", target)
|
|
322
322
|
if match:
|
|
323
323
|
return match.group(1)
|
|
324
324
|
else:
|
|
@@ -2,7 +2,9 @@ from databend_sqlalchemy import connector
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
def test():
|
|
5
|
-
conn = connector.connect(
|
|
5
|
+
conn = connector.connect(
|
|
6
|
+
"databend://databend:databend@localhost:8000/default?sslmode=disable"
|
|
7
|
+
)
|
|
6
8
|
cursor = conn.cursor()
|
|
7
9
|
cursor.execute(
|
|
8
10
|
"select null as db, name as name, database as schema, if(engine = 'VIEW', 'view', 'table') "
|
{databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: databend-sqlalchemy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Sqlalchemy adapter for Databend
|
|
5
5
|
Home-page: https://github.com/datafuselabs/databend-sqlalchemy
|
|
6
6
|
Author: Databend Cloud
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
19
19
|
Requires-Python: >=3.7
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: databend_py>=0.
|
|
22
|
+
Requires-Dist: databend_py>=0.5.0
|
|
23
23
|
Requires-Dist: sqlalchemy<2.0,>1.3.21
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: devtools==0.7.0; extra == "dev"
|
|
@@ -51,14 +51,14 @@ The DSN format is similar to that of regular Postgres::
|
|
|
51
51
|
from sqlalchemy import create_engine, text
|
|
52
52
|
from sqlalchemy.engine.base import Connection, Engine
|
|
53
53
|
engine = create_engine(
|
|
54
|
-
f"databend://{username}:{password}@{host_port_name}/{database_name}?
|
|
54
|
+
f"databend://{username}:{password}@{host_port_name}/{database_name}?sslmode=disable"
|
|
55
55
|
)
|
|
56
56
|
connection = engine.connect()
|
|
57
57
|
result = connection.execute(text("SELECT 1"))
|
|
58
58
|
assert len(result.fetchall()) == 1
|
|
59
59
|
|
|
60
60
|
import connector
|
|
61
|
-
cursor = connector.connect('
|
|
61
|
+
cursor = connector.connect('databend://root:@localhost:8000?sslmode=disable').cursor()
|
|
62
62
|
cursor.execute('SELECT * FROM test')
|
|
63
63
|
# print(cursor.fetchone())
|
|
64
64
|
# print(cursor.fetchall())
|
{databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy.egg-info/SOURCES.txt
RENAMED
|
@@ -7,10 +7,8 @@ setup.cfg
|
|
|
7
7
|
./databend_sqlalchemy/__init__.py
|
|
8
8
|
./databend_sqlalchemy/connector.py
|
|
9
9
|
./databend_sqlalchemy/databend_dialect.py
|
|
10
|
-
./databend_sqlalchemy/entry_points.py
|
|
11
10
|
./databend_sqlalchemy/errors.py
|
|
12
11
|
./databend_sqlalchemy/test.py
|
|
13
|
-
./tests/__init__.py
|
|
14
12
|
databend_sqlalchemy.egg-info/PKG-INFO
|
|
15
13
|
databend_sqlalchemy.egg-info/SOURCES.txt
|
|
16
14
|
databend_sqlalchemy.egg-info/dependency_links.txt
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = [
|
|
3
|
-
"setuptools>=42",
|
|
4
|
-
"wheel"
|
|
5
|
-
]
|
|
2
|
+
requires = ["setuptools>=42", "wheel"]
|
|
6
3
|
build-backend = "setuptools.build_meta"
|
|
7
4
|
|
|
8
5
|
# configure isort to be compatible with black
|
|
@@ -21,4 +18,4 @@ exclude = '''
|
|
|
21
18
|
| \.venv
|
|
22
19
|
| dist
|
|
23
20
|
)/
|
|
24
|
-
'''
|
|
21
|
+
'''
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
import sys
|
|
4
|
-
import pkg_resources
|
|
5
|
-
|
|
6
|
-
EXPECTED_EPS = {"sqlalchemy.dialects:databend", "superset.db_engine_specs:databend"}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def validate_entrypoints():
|
|
10
|
-
expected_eps = EXPECTED_EPS.copy()
|
|
11
|
-
try:
|
|
12
|
-
dist = pkg_resources.get_distribution("databend-sqlalchemy")
|
|
13
|
-
except pkg_resources.DistributionNotFound:
|
|
14
|
-
print("\nDatabend Sqlalchemy package not found in this Python installation")
|
|
15
|
-
return -1
|
|
16
|
-
entry_map = dist.get_entry_map()
|
|
17
|
-
print()
|
|
18
|
-
for ep_group, entry_points in entry_map.items():
|
|
19
|
-
print(ep_group)
|
|
20
|
-
for entry_point in entry_points.values():
|
|
21
|
-
print(
|
|
22
|
-
f' {entry_point.name}={entry_point.module_name}.{", ".join(entry_point.attrs)}'
|
|
23
|
-
)
|
|
24
|
-
name = f"{ep_group}:{entry_point.name}"
|
|
25
|
-
try:
|
|
26
|
-
expected_eps.remove(name)
|
|
27
|
-
except KeyError:
|
|
28
|
-
print(f"\nUnexpected entry point {name} found")
|
|
29
|
-
return -1
|
|
30
|
-
if expected_eps:
|
|
31
|
-
print()
|
|
32
|
-
for name in expected_eps:
|
|
33
|
-
print(f"Did not find expected ep {name}")
|
|
34
|
-
return -1
|
|
35
|
-
print("\nEntrypoints correctly installed")
|
|
36
|
-
return 0
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if __name__ == "__main__":
|
|
40
|
-
sys.exit(validate_entrypoints())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|