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.
Files changed (21) hide show
  1. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/PKG-INFO +4 -4
  2. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/README.rst +2 -2
  3. databend_sqlalchemy-0.4.0/databend_sqlalchemy/__init__.py +5 -0
  4. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy/connector.py +44 -52
  5. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy/databend_dialect.py +7 -7
  6. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy/test.py +3 -1
  7. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy.egg-info/PKG-INFO +4 -4
  8. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy.egg-info/SOURCES.txt +0 -2
  9. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy.egg-info/requires.txt +1 -1
  10. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy.egg-info/top_level.txt +0 -1
  11. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/pyproject.toml +2 -5
  12. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/setup.cfg +1 -1
  13. databend_sqlalchemy-0.3.2/databend_sqlalchemy/__init__.py +0 -7
  14. databend_sqlalchemy-0.3.2/databend_sqlalchemy/entry_points.py +0 -40
  15. databend_sqlalchemy-0.3.2/tests/__init__.py +0 -0
  16. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/LICENSE +0 -0
  17. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/cc_superset/__init__.py +0 -0
  18. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/cc_superset/engine.py +0 -0
  19. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy/errors.py +0 -0
  20. {databend_sqlalchemy-0.3.2 → databend_sqlalchemy-0.4.0}/databend_sqlalchemy.egg-info/dependency_links.txt +0 -0
  21. {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.2
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.4.9
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}?secure=false"
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('http://root:@localhost:8081').cursor()
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}?secure=false"
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('http://root:@localhost:8081').cursor()
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())
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env python
2
+
3
+
4
+ VERSION = (0, 4, 0)
5
+ __version__ = ".".join(str(x) for x in VERSION)
@@ -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 Client
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(Client):
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, db_url="http://root:@localhost:8081"):
105
- super(Connection, self).__init__(db_url)
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
- self.client.disconnect()
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, connector):
135
- self._db = connector.client
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._data = None
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
- column_types, response = self._db.execute(
187
- operation, parameters, with_column_types=True
188
- )
189
- self._process_response(column_types, response)
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._data:
225
- return None
226
+ if not self._rows:
227
+ raise Exception("No rows yet")
226
228
  else:
227
229
  self._rownumber += 1
228
- return self._data.pop(0)
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
- if not self._data:
246
- return []
247
- else:
248
- if len(self._data) > size:
249
- result, self._data = self._data[:size], self._data[size:]
250
- else:
251
- result, self._data = self._data, []
252
- self._rownumber += len(result)
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
- if not self._data:
263
- return []
264
- else:
265
- result, self._data = self._data, []
266
- self._rownumber += len(result)
267
- return result
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
- one = self.fetchone()
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
- assert self._state == self._STATE_SUCCEEDED, "Query should be finished"
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._data = None
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
@@ -158,7 +158,7 @@ class DatabendCompiler(PGCompiler):
158
158
  )
159
159
 
160
160
  def visit_column(
161
- self, column, add_to_result_map=None, include_table=True, **kwargs
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
- self, context: Optional[ExecutionContext] = None, *args: Any, **kwargs: Any
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
- "db_url": "databend://%s:%s@%s:%d/%s"
260
- % (url.username, url.password, url.host, url.port or 8000, url.database),
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["db_url"] = kwargs["db_url"] + "?" + k + "=" + v
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'Nullable\(([^)]+)\)', target)
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("http://databend:databend@localhost:8000/default?secure=false")
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') "
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: databend-sqlalchemy
3
- Version: 0.3.2
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.4.9
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}?secure=false"
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('http://root:@localhost:8081').cursor()
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())
@@ -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,4 +1,4 @@
1
- databend_py>=0.4.9
1
+ databend_py>=0.5.0
2
2
  sqlalchemy<2.0,>1.3.21
3
3
 
4
4
  [dev]
@@ -1,3 +1,2 @@
1
1
  cc_superset
2
2
  databend_sqlalchemy
3
- tests
@@ -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
+ '''
@@ -25,7 +25,7 @@ project_urls =
25
25
  [options]
26
26
  packages = find:
27
27
  install_requires =
28
- databend_py>=0.4.9
28
+ databend_py>=0.5.0
29
29
  sqlalchemy>1.3.21,<2.0
30
30
  python_requires = >=3.7
31
31
  package_dir =
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env python
2
-
3
-
4
- from databend_sqlalchemy.entry_points import validate_entrypoints # noqa
5
-
6
- VERSION = (0, 3, 2)
7
- __version__ = ".".join(str(x) for x in VERSION)
@@ -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