chdb 2.1.1__cp39-cp39-macosx_11_0_arm64.whl → 2.2.0b1__cp39-cp39-macosx_11_0_arm64.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.

Potentially problematic release.


This version of chdb might be problematic. Click here for more details.

chdb/__init__.py CHANGED
@@ -18,7 +18,7 @@ _process_result_format_funs = {
18
18
  # UDF script path will be f"{g_udf_path}/{func_name}.py"
19
19
  g_udf_path = ""
20
20
 
21
- chdb_version = ('2', '1', '1')
21
+ chdb_version = ('2', '2', '0b1')
22
22
  if sys.version_info[:2] >= (3, 7):
23
23
  # get the path of the current file
24
24
  current_path = os.path.dirname(os.path.abspath(__file__))
@@ -85,8 +85,10 @@ sql = query
85
85
  PyReader = _chdb.PyReader
86
86
 
87
87
  from . import dbapi, session, udf, utils # noqa: E402
88
+ from .state import connect # noqa: E402
88
89
 
89
90
  __all__ = [
91
+ "_chdb",
90
92
  "PyReader",
91
93
  "ChdbError",
92
94
  "query",
@@ -99,4 +101,5 @@ __all__ = [
99
101
  "session",
100
102
  "udf",
101
103
  "utils",
104
+ "connect",
102
105
  ]
Binary file
chdb/dbapi/connections.py CHANGED
@@ -1,7 +1,7 @@
1
- import json
2
1
  from . import err
3
2
  from .cursors import Cursor
4
3
  from . import converters
4
+ from ..state import sqlitelike as chdb_stateful
5
5
 
6
6
  DEBUG = False
7
7
  VERBOSE = False
@@ -10,56 +10,29 @@ VERBOSE = False
10
10
  class Connection(object):
11
11
  """
12
12
  Representation of a connection with chdb.
13
-
14
- The proper way to get an instance of this class is to call
15
- connect().
16
-
17
- Accepts several arguments:
18
-
19
- :param cursorclass: Custom cursor class to use.
20
- :param path: Optional folder path to store database files on disk.
21
-
22
- See `Connection <https://www.python.org/dev/peps/pep-0249/#connection-objects>`_ in the
23
- specification.
24
13
  """
25
14
 
26
- _closed = False
27
- _session = None
28
-
29
- def __init__(self, cursorclass=Cursor, path=None):
30
-
31
- self._resp = None
32
-
33
- # 1. pre-process params in init
34
- self.encoding = 'utf8'
35
-
36
- self.cursorclass = cursorclass
37
-
38
- self._result = None
15
+ def __init__(self, path=None):
16
+ self._closed = False
17
+ self.encoding = "utf8"
39
18
  self._affected_rows = 0
19
+ self._resp = None
40
20
 
41
- self.connect(path)
21
+ # Initialize sqlitelike connection
22
+ connection_string = ":memory:" if path is None else f"file:{path}"
23
+ self._conn = chdb_stateful.Connection(connection_string)
42
24
 
43
- def connect(self, path=None):
44
- from chdb import session as chs
45
- self._session = chs.Session(path)
46
- self._closed = False
47
- self._execute_command("select 1;")
48
- self._read_query_result()
25
+ # Test connection with a simple query
26
+ cursor = self._conn.cursor()
27
+ cursor.execute("SELECT 1")
28
+ cursor.close()
49
29
 
50
30
  def close(self):
51
- """
52
- Send the quit message and close the socket.
53
-
54
- See `Connection.close() <https://www.python.org/dev/peps/pep-0249/#Connection.close>`_
55
- in the specification.
56
-
57
- :raise Error: If the connection is already closed.
58
- """
31
+ """Send the quit message and close the socket."""
59
32
  if self._closed:
60
33
  raise err.Error("Already closed")
61
34
  self._closed = True
62
- self._session = None
35
+ self._conn.close()
63
36
 
64
37
  @property
65
38
  def open(self):
@@ -67,81 +40,41 @@ class Connection(object):
67
40
  return not self._closed
68
41
 
69
42
  def commit(self):
70
- """
71
- Commit changes to stable storage.
72
-
73
- See `Connection.commit() <https://www.python.org/dev/peps/pep-0249/#commit>`_
74
- in the specification.
75
- """
76
- return
43
+ """Commit changes to stable storage."""
44
+ # No-op for ClickHouse
45
+ pass
77
46
 
78
47
  def rollback(self):
79
- """
80
- Roll back the current transaction.
81
-
82
- See `Connection.rollback() <https://www.python.org/dev/peps/pep-0249/#rollback>`_
83
- in the specification.
84
- """
85
- return
48
+ """Roll back the current transaction."""
49
+ # No-op for ClickHouse
50
+ pass
86
51
 
87
52
  def cursor(self, cursor=None):
88
- """
89
- Create a new cursor to execute queries with.
90
-
91
- :param cursor: The type of cursor to create; current only :py:class:`Cursor`
92
- None means use Cursor.
93
- """
53
+ """Create a new cursor to execute queries with."""
54
+ if self._closed:
55
+ raise err.Error("Connection closed")
94
56
  if cursor:
95
- return cursor(self)
96
- return self.cursorclass(self)
57
+ return Cursor(self)
58
+ return Cursor(self)
97
59
 
98
- # The following methods are INTERNAL USE ONLY (called from Cursor)
99
- def query(self, sql):
100
- if isinstance(sql, str):
101
- sql = sql.encode(self.encoding, 'surrogateescape')
102
- self._execute_command(sql)
103
- self._affected_rows = self._read_query_result()
104
- return self._affected_rows
105
-
106
- def _execute_command(self, sql):
107
- """
108
- :raise InterfaceError: If the connection is closed.
109
- :raise ValueError: If no username was specified.
110
- """
60
+ def query(self, sql, fmt="ArrowStream"):
61
+ """Execute a query and return the raw result."""
111
62
  if self._closed:
112
63
  raise err.InterfaceError("Connection closed")
113
64
 
114
65
  if isinstance(sql, str):
115
- sql = sql.encode(self.encoding)
66
+ sql = sql.encode(self.encoding, "surrogateescape")
116
67
 
117
- if isinstance(sql, bytearray):
118
- sql = bytes(sql)
119
-
120
- # drop last command return
121
- if self._resp is not None:
122
- self._resp = None
123
-
124
- if DEBUG:
125
- print("DEBUG: query:", sql)
126
68
  try:
127
- res = self._session.query(sql, fmt="JSON")
128
- if res.has_error():
129
- raise err.DatabaseError(res.error_message())
130
- self._resp = res.data()
69
+ result = self._conn.query(sql.decode(), fmt)
70
+ self._resp = result
71
+ return result
131
72
  except Exception as error:
132
- raise err.InterfaceError("query err: %s" % error)
73
+ raise err.InterfaceError(f"Query error: {error}")
133
74
 
134
75
  def escape(self, obj, mapping=None):
135
- """Escape whatever value you pass to it.
136
-
137
- Non-standard, for internal use; do not use this in your applications.
138
- """
139
- if isinstance(obj, str):
140
- return "'" + self.escape_string(obj) + "'"
141
- if isinstance(obj, (bytes, bytearray)):
142
- ret = self._quote_bytes(obj)
143
- return ret
144
- return converters.escape_item(obj, mapping=mapping)
76
+ """Escape whatever value you pass to it."""
77
+ return converters.escape_item(obj, mapping)
145
78
 
146
79
  def escape_string(self, s):
147
80
  return converters.escape_string(s)
@@ -149,13 +82,6 @@ class Connection(object):
149
82
  def _quote_bytes(self, s):
150
83
  return converters.escape_bytes(s)
151
84
 
152
- def _read_query_result(self):
153
- self._result = None
154
- result = CHDBResult(self)
155
- result.read()
156
- self._result = result
157
- return result.affected_rows
158
-
159
85
  def __enter__(self):
160
86
  """Context manager that returns a Cursor"""
161
87
  return self.cursor()
@@ -166,52 +92,9 @@ class Connection(object):
166
92
  self.rollback()
167
93
  else:
168
94
  self.commit()
95
+ self.close()
169
96
 
170
97
  @property
171
98
  def resp(self):
99
+ """Return the last query response"""
172
100
  return self._resp
173
-
174
-
175
- class CHDBResult(object):
176
- def __init__(self, connection):
177
- """
178
- :type connection: Connection
179
- """
180
- self.connection = connection
181
- self.affected_rows = 0
182
- self.insert_id = None
183
- self.warning_count = 0
184
- self.message = None
185
- self.field_count = 0
186
- self.description = None
187
- self.rows = None
188
- self.has_next = None
189
-
190
- def read(self):
191
- # Handle empty responses (for instance from CREATE TABLE)
192
- if self.connection.resp is None:
193
- return
194
-
195
- try:
196
- data = json.loads(self.connection.resp)
197
- except Exception as error:
198
- raise err.InterfaceError("Unsupported response format:" % error)
199
-
200
- try:
201
- self.field_count = len(data["meta"])
202
- description = []
203
- for meta in data["meta"]:
204
- fields = [meta["name"], meta["type"]]
205
- description.append(tuple(fields))
206
- self.description = tuple(description)
207
-
208
- rows = []
209
- for line in data["data"]:
210
- row = []
211
- for i in range(self.field_count):
212
- column_data = converters.convert_column_data(self.description[i][1], line[self.description[i][0]])
213
- row.append(column_data)
214
- rows.append(tuple(row))
215
- self.rows = tuple(rows)
216
- except Exception as error:
217
- raise err.InterfaceError("Read return data err:" % error)
chdb/dbapi/cursors.py CHANGED
@@ -29,13 +29,11 @@ class Cursor(object):
29
29
 
30
30
  def __init__(self, connection):
31
31
  self.connection = connection
32
+ self._cursor = connection._conn.cursor()
32
33
  self.description = None
33
34
  self.rowcount = -1
34
- self.rownumber = 0
35
35
  self.arraysize = 1
36
36
  self.lastrowid = None
37
- self._result = None
38
- self._rows = None
39
37
  self._executed = None
40
38
 
41
39
  def __enter__(self):
@@ -83,14 +81,7 @@ class Cursor(object):
83
81
  """
84
82
  Closing a cursor just exhausts all remaining data.
85
83
  """
86
- conn = self.connection
87
- if conn is None:
88
- return
89
- try:
90
- while self.nextset():
91
- pass
92
- finally:
93
- self.connection = None
84
+ self._cursor.close()
94
85
 
95
86
  def _get_db(self):
96
87
  if not self.connection:
@@ -121,33 +112,6 @@ class Cursor(object):
121
112
 
122
113
  return query
123
114
 
124
- def _clear_result(self):
125
- self.rownumber = 0
126
- self._result = None
127
-
128
- self.rowcount = 0
129
- self.description = None
130
- self.lastrowid = None
131
- self._rows = None
132
-
133
- def _do_get_result(self):
134
- conn = self._get_db()
135
-
136
- self._result = result = conn._result
137
-
138
- self.rowcount = result.affected_rows
139
- self.description = result.description
140
- self.lastrowid = result.insert_id
141
- self._rows = result.rows
142
-
143
- def _query(self, q):
144
- conn = self._get_db()
145
- self._last_executed = q
146
- self._clear_result()
147
- conn.query(q)
148
- self._do_get_result()
149
- return self.rowcount
150
-
151
115
  def execute(self, query, args=None):
152
116
  """Execute a query
153
117
 
@@ -162,14 +126,24 @@ class Cursor(object):
162
126
  If args is a list or tuple, %s can be used as a placeholder in the query.
163
127
  If args is a dict, %(name)s can be used as a placeholder in the query.
164
128
  """
165
- while self.nextset():
166
- pass
129
+ if args is not None:
130
+ query = query % self._escape_args(args, self.connection)
131
+
132
+ self._cursor.execute(query)
167
133
 
168
- query = self.mogrify(query, args)
134
+ # Get description from Arrow schema
135
+ if self._cursor._current_table is not None:
136
+ self.description = [
137
+ (field.name, field.type.to_pandas_dtype(), None, None, None, None, None)
138
+ for field in self._cursor._current_table.schema
139
+ ]
140
+ self.rowcount = self._cursor._current_table.num_rows
141
+ else:
142
+ self.description = None
143
+ self.rowcount = -1
169
144
 
170
- result = self._query(query)
171
145
  self._executed = query
172
- return result
146
+ return self.rowcount
173
147
 
174
148
  def executemany(self, query, args):
175
149
  # type: (str, list) -> int
@@ -233,34 +207,21 @@ class Cursor(object):
233
207
 
234
208
  def fetchone(self):
235
209
  """Fetch the next row"""
236
- self._check_executed()
237
- if self._rows is None or self.rownumber >= len(self._rows):
238
- return None
239
- result = self._rows[self.rownumber]
240
- self.rownumber += 1
241
- return result
242
-
243
- def fetchmany(self, size=None):
210
+ if not self._executed:
211
+ raise err.ProgrammingError("execute() first")
212
+ return self._cursor.fetchone()
213
+
214
+ def fetchmany(self, size=1):
244
215
  """Fetch several rows"""
245
- self._check_executed()
246
- if self._rows is None:
247
- return ()
248
- end = self.rownumber + (size or self.arraysize)
249
- result = self._rows[self.rownumber:end]
250
- self.rownumber = min(end, len(self._rows))
251
- return result
216
+ if not self._executed:
217
+ raise err.ProgrammingError("execute() first")
218
+ return self._cursor.fetchmany(size)
252
219
 
253
220
  def fetchall(self):
254
221
  """Fetch all the rows"""
255
- self._check_executed()
256
- if self._rows is None:
257
- return ()
258
- if self.rownumber:
259
- result = self._rows[self.rownumber:]
260
- else:
261
- result = self._rows
262
- self.rownumber = len(self._rows)
263
- return result
222
+ if not self._executed:
223
+ raise err.ProgrammingError("execute() first")
224
+ return self._cursor.fetchall()
264
225
 
265
226
  def nextset(self):
266
227
  """Get the next query set"""
@@ -272,26 +233,3 @@ class Cursor(object):
272
233
 
273
234
  def setoutputsizes(self, *args):
274
235
  """Does nothing, required by DB API."""
275
-
276
-
277
- class DictCursor(Cursor):
278
- """A cursor which returns results as a dictionary"""
279
- # You can override this to use OrderedDict or other dict-like types.
280
- dict_type = dict
281
-
282
- def _do_get_result(self):
283
- super()._do_get_result()
284
- fields = []
285
- if self.description:
286
- for f in self.description:
287
- name = f[0]
288
- fields.append(name)
289
- self._fields = fields
290
-
291
- if fields and self._rows:
292
- self._rows = [self._conv_row(r) for r in self._rows]
293
-
294
- def _conv_row(self, row):
295
- if row is None:
296
- return None
297
- return self.dict_type(zip(self._fields, row))
chdb/state/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .sqlitelike import connect
2
+
3
+ __all__ = ["connect"]
@@ -0,0 +1,132 @@
1
+ import io
2
+ from typing import Optional, Any
3
+ from chdb import _chdb
4
+
5
+ # try import pyarrow if failed, raise ImportError with suggestion
6
+ try:
7
+ import pyarrow as pa # noqa
8
+ except ImportError as e:
9
+ print(f"ImportError: {e}")
10
+ print('Please install pyarrow via "pip install pyarrow"')
11
+ raise ImportError("Failed to import pyarrow") from None
12
+
13
+
14
+ class Connection:
15
+ def __init__(self, connection_string: str):
16
+ # print("Connection", connection_string)
17
+ self._cursor: Optional[Cursor] = None
18
+ self._conn = _chdb.connect(connection_string)
19
+
20
+ def cursor(self) -> "Cursor":
21
+ self._cursor = Cursor(self._conn)
22
+ return self._cursor
23
+
24
+ def query(self, query: str, format: str = "ArrowStream") -> Any:
25
+ return self._conn.query(query, format)
26
+
27
+ def close(self) -> None:
28
+ # print("close")
29
+ if self._cursor:
30
+ self._cursor.close()
31
+ self._conn.close()
32
+
33
+
34
+ class Cursor:
35
+ def __init__(self, connection):
36
+ self._conn = connection
37
+ self._cursor = self._conn.cursor()
38
+ self._current_table: Optional[pa.Table] = None
39
+ self._current_row: int = 0
40
+
41
+ def execute(self, query: str) -> None:
42
+ self._cursor.execute(query)
43
+ result_mv = self._cursor.get_memview()
44
+ # print("get_result", result_mv)
45
+ if self._cursor.has_error():
46
+ raise Exception(self._cursor.error_message())
47
+ if self._cursor.data_size() == 0:
48
+ self._current_table = None
49
+ self._current_row = 0
50
+ return
51
+ arrow_data = result_mv.tobytes()
52
+ reader = pa.ipc.open_stream(io.BytesIO(arrow_data))
53
+ self._current_table = reader.read_all()
54
+ self._current_row = 0
55
+
56
+ def commit(self) -> None:
57
+ self._cursor.commit()
58
+
59
+ def fetchone(self) -> Optional[tuple]:
60
+ if not self._current_table or self._current_row >= len(self._current_table):
61
+ return None
62
+
63
+ row_dict = {
64
+ col: self._current_table.column(col)[self._current_row].as_py()
65
+ for col in self._current_table.column_names
66
+ }
67
+ self._current_row += 1
68
+ return tuple(row_dict.values())
69
+
70
+ def fetchmany(self, size: int = 1) -> tuple:
71
+ if not self._current_table:
72
+ return tuple()
73
+
74
+ rows = []
75
+ for _ in range(size):
76
+ if (row := self.fetchone()) is None:
77
+ break
78
+ rows.append(row)
79
+ return tuple(rows)
80
+
81
+ def fetchall(self) -> tuple:
82
+ if not self._current_table:
83
+ return tuple()
84
+
85
+ remaining_rows = []
86
+ while (row := self.fetchone()) is not None:
87
+ remaining_rows.append(row)
88
+ return tuple(remaining_rows)
89
+
90
+ def close(self) -> None:
91
+ self._cursor.close()
92
+
93
+ def __iter__(self):
94
+ return self
95
+
96
+ def __next__(self) -> tuple:
97
+ row = self.fetchone()
98
+ if row is None:
99
+ raise StopIteration
100
+ return row
101
+
102
+
103
+ def connect(connection_string: str = ":memory:") -> Connection:
104
+ """
105
+ Create a connection to chDB backgroud server.
106
+ Only one open connection is allowed per process. Use `close` to close the connection.
107
+ If called with the same connection string, the same connection object will be returned.
108
+ You can use the connection object to create cursor object. `cursor` method will return a cursor object.
109
+
110
+ Args:
111
+ connection_string (str, optional): Connection string. Defaults to ":memory:".
112
+ Aslo support file path like:
113
+ - ":memory:" (for in-memory database)
114
+ - "test.db" (for relative path)
115
+ - "file:test.db" (same as above)
116
+ - "/path/to/test.db" (for absolute path)
117
+ - "file:/path/to/test.db" (same as above)
118
+ - "file:test.db?param1=value1&param2=value2" (for relative path with query params)
119
+ - "///path/to/test.db?param1=value1&param2=value2" (for absolute path)
120
+
121
+ Connection string args handling:
122
+ Connection string can contain query params like "file:test.db?param1=value1&param2=value2"
123
+ "param1=value1" will be passed to ClickHouse engine as start up args.
124
+
125
+ For more details, see `clickhouse local --help --verbose`
126
+ Some special args handling:
127
+ - "mode=ro" would be "--readonly=1" for clickhouse (read-only mode)
128
+
129
+ Returns:
130
+ Connection: Connection object
131
+ """
132
+ return Connection(connection_string)
chdb/utils/__init__.py CHANGED
@@ -5,4 +5,5 @@ __all__ = [ # noqa: F405
5
5
  "convert_to_columnar",
6
6
  "infer_data_type",
7
7
  "infer_data_types",
8
+ "trace",
8
9
  ]
chdb/utils/trace.py ADDED
@@ -0,0 +1,74 @@
1
+ import functools
2
+ import inspect
3
+ import sys
4
+ import linecache
5
+ from datetime import datetime
6
+
7
+ enable_print = False
8
+
9
+
10
+ def print_lines(func):
11
+ if not enable_print:
12
+ return func
13
+
14
+ @functools.wraps(func)
15
+ def wrapper(*args, **kwargs):
16
+ # Get function name and determine if it's a method
17
+ is_method = inspect.ismethod(func) or (
18
+ len(args) > 0 and hasattr(args[0].__class__, func.__name__)
19
+ )
20
+ class_name = args[0].__class__.__name__ if is_method else None # type: ignore
21
+
22
+ # Get the source code of the function
23
+ try:
24
+ source_lines, start_line = inspect.getsourcelines(func)
25
+ except OSError:
26
+ # Handle cases where source might not be available
27
+ print(f"Warning: Could not get source for {func.__name__}")
28
+ return func(*args, **kwargs)
29
+
30
+ def trace(frame, event, arg):
31
+ if event == "line":
32
+ # Get the current line number and code
33
+ line_no = frame.f_lineno
34
+ line = linecache.getline(frame.f_code.co_filename, line_no).strip()
35
+
36
+ # Don't print decorator lines or empty lines
37
+ if line and not line.startswith("@"):
38
+ # Get local variables
39
+ local_vars = frame.f_locals.copy()
40
+ if is_method:
41
+ # Remove 'self' from local variables for clarity
42
+ local_vars.pop("self", None)
43
+
44
+ # Format timestamp
45
+ timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]
46
+
47
+ # Create context string (class.method or function)
48
+ context = (
49
+ f"{class_name}.{func.__name__}" if class_name else func.__name__
50
+ )
51
+
52
+ # Print execution information
53
+ print(f"[{timestamp}] {context} line {line_no}: {line}")
54
+
55
+ # Print local variables if they exist and have changed
56
+ if local_vars:
57
+ vars_str = ", ".join(
58
+ f"{k}={repr(v)}" for k, v in local_vars.items()
59
+ )
60
+ print(f" Variables: {vars_str}")
61
+ return trace
62
+
63
+ # Set the trace function
64
+ sys.settrace(trace)
65
+
66
+ # Call the original function
67
+ result = func(*args, **kwargs)
68
+
69
+ # Disable tracing
70
+ sys.settrace(None)
71
+
72
+ return result
73
+
74
+ return wrapper
@@ -1,12 +1,15 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: chdb
3
- Version: 2.1.1
3
+ Version: 2.2.0b1
4
4
  Summary: chDB is an in-process SQL OLAP Engine powered by ClickHouse
5
5
  Home-page: https://github.com/chdb-io/chdb
6
6
  Author: auxten
7
7
  Author-email: auxten@clickhouse.com
8
8
  License: Apache-2.0
9
- Project-URL: Documentation, https://doc.chdb.io/
9
+ Project-URL: Homepage, https://clickhouse.com/chdb
10
+ Project-URL: Documentation, https://clickhouse.com/docs/en/chdb
11
+ Project-URL: Source, https://github.com/chdb-io/chdb
12
+ Project-URL: Download, https://pypi.org/project/chdb/#files
10
13
  Project-URL: Twitter, https://twitter.com/chdb_io
11
14
  Platform: Mac
12
15
  Platform: Linux
@@ -19,11 +22,14 @@ Classifier: Programming Language :: Python :: 3.8
19
22
  Classifier: Programming Language :: Python :: 3.9
20
23
  Classifier: Programming Language :: Python :: 3.10
21
24
  Classifier: Programming Language :: Python :: 3.11
25
+ Classifier: Programming Language :: Python :: 3.12
22
26
  Classifier: Topic :: Database
23
27
  Classifier: Topic :: Scientific/Engineering :: Information Analysis
24
28
  Requires-Python: >=3.8
25
29
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
26
30
  License-File: LICENSE.txt
31
+ Requires-Dist: pyarrow>=13.0.0
32
+ Requires-Dist: pandas>=2.0.0
27
33
 
28
34
  <div align="center">
29
35
  <a href="https://clickhouse.com/blog/chdb-joins-clickhouse-family">📢 chDB joins the ClickHouse family 🐍+🚀</a>
@@ -312,8 +318,11 @@ For more examples, see [examples](examples) and [tests](tests).
312
318
 
313
319
  - [chDB vs Pandas](https://colab.research.google.com/drive/1FogLujJ_-ds7RGurDrUnK-U0IW8a8Qd0)
314
320
 
321
+ - [Benchmark on DataFrame: chDB Pandas DuckDB Polars](https://benchmark.clickhouse.com/#eyJzeXN0ZW0iOnsiQWxsb3lEQiI6dHJ1ZSwiQWxsb3lEQiAodHVuZWQpIjp0cnVlLCJBdGhlbmEgKHBhcnRpdGlvbmVkKSI6dHJ1ZSwiQXRoZW5hIChzaW5nbGUpIjp0cnVlLCJBdXJvcmEgZm9yIE15U1FMIjp0cnVlLCJBdXJvcmEgZm9yIFBvc3RncmVTUUwiOnRydWUsIkJ5Q29uaXR5Ijp0cnVlLCJCeXRlSG91c2UiOnRydWUsImNoREIgKERhdGFGcmFtZSkiOnRydWUsImNoREIgKFBhcnF1ZXQsIHBhcnRpdGlvbmVkKSI6dHJ1ZSwiY2hEQiI6dHJ1ZSwiQ2l0dXMiOnRydWUsIkNsaWNrSG91c2UgQ2xvdWQgKGF3cykiOnRydWUsIkNsaWNrSG91c2UgQ2xvdWQgKGF6dXJlKSI6dHJ1ZSwiQ2xpY2tIb3VzZSBDbG91ZCAoZ2NwKSI6dHJ1ZSwiQ2xpY2tIb3VzZSAoZGF0YSBsYWtlLCBwYXJ0aXRpb25lZCkiOnRydWUsIkNsaWNrSG91c2UgKGRhdGEgbGFrZSwgc2luZ2xlKSI6dHJ1ZSwiQ2xpY2tIb3VzZSAoUGFycXVldCwgcGFydGl0aW9uZWQpIjp0cnVlLCJDbGlja0hvdXNlIChQYXJxdWV0LCBzaW5nbGUpIjp0cnVlLCJDbGlja0hvdXNlICh3ZWIpIjp0cnVlLCJDbGlja0hvdXNlIjp0cnVlLCJDbGlja0hvdXNlICh0dW5lZCkiOnRydWUsIkNsaWNrSG91c2UgKHR1bmVkLCBtZW1vcnkpIjp0cnVlLCJDbG91ZGJlcnJ5Ijp0cnVlLCJDcmF0ZURCIjp0cnVlLCJDcnVuY2h5IEJyaWRnZSBmb3IgQW5hbHl0aWNzIChQYXJxdWV0KSI6dHJ1ZSwiRGF0YWJlbmQiOnRydWUsIkRhdGFGdXNpb24gKFBhcnF1ZXQsIHBhcnRpdGlvbmVkKSI6dHJ1ZSwiRGF0YUZ1c2lvbiAoUGFycXVldCwgc2luZ2xlKSI6dHJ1ZSwiQXBhY2hlIERvcmlzIjp0cnVlLCJEcnVpZCI6dHJ1ZSwiRHVja0RCIChEYXRhRnJhbWUpIjp0cnVlLCJEdWNrREIgKFBhcnF1ZXQsIHBhcnRpdGlvbmVkKSI6dHJ1ZSwiRHVja0RCIjp0cnVlLCJFbGFzdGljc2VhcmNoIjp0cnVlLCJFbGFzdGljc2VhcmNoICh0dW5lZCkiOmZhbHNlLCJHbGFyZURCIjp0cnVlLCJHcmVlbnBsdW0iOnRydWUsIkhlYXZ5QUkiOnRydWUsIkh5ZHJhIjp0cnVlLCJJbmZvYnJpZ2h0Ijp0cnVlLCJLaW5ldGljYSI6dHJ1ZSwiTWFyaWFEQiBDb2x1bW5TdG9yZSI6dHJ1ZSwiTWFyaWFEQiI6ZmFsc2UsIk1vbmV0REIiOnRydWUsIk1vbmdvREIiOnRydWUsIk1vdGhlcmR1Y2siOnRydWUsIk15U1FMIChNeUlTQU0pIjp0cnVlLCJNeVNRTCI6dHJ1ZSwiT3hsYSI6dHJ1ZSwiUGFuZGFzIChEYXRhRnJhbWUpIjp0cnVlLCJQYXJhZGVEQiAoUGFycXVldCwgcGFydGl0aW9uZWQpIjp0cnVlLCJQYXJhZGVEQiAoUGFycXVldCwgc2luZ2xlKSI6dHJ1ZSwiUGlub3QiOnRydWUsIlBvbGFycyAoRGF0YUZyYW1lKSI6dHJ1ZSwiUG9zdGdyZVNRTCAodHVuZWQpIjpmYWxzZSwiUG9zdGdyZVNRTCI6dHJ1ZSwiUXVlc3REQiAocGFydGl0aW9uZWQpIjp0cnVlLCJRdWVzdERCIjp0cnVlLCJSZWRzaGlmdCI6dHJ1ZSwiU2luZ2xlU3RvcmUiOnRydWUsIlNub3dmbGFrZSI6dHJ1ZSwiU1FMaXRlIjp0cnVlLCJTdGFyUm9ja3MiOnRydWUsIlRhYmxlc3BhY2UiOnRydWUsIlRlbWJvIE9MQVAgKGNvbHVtbmFyKSI6dHJ1ZSwiVGltZXNjYWxlREIgKGNvbXByZXNzaW9uKSI6dHJ1ZSwiVGltZXNjYWxlREIiOnRydWUsIlVtYnJhIjp0cnVlfSwidHlwZSI6eyJDIjpmYWxzZSwiY29sdW1uLW9yaWVudGVkIjpmYWxzZSwiUG9zdGdyZVNRTCBjb21wYXRpYmxlIjpmYWxzZSwibWFuYWdlZCI6ZmFsc2UsImdjcCI6ZmFsc2UsInN0YXRlbGVzcyI6ZmFsc2UsIkphdmEiOmZhbHNlLCJDKysiOmZhbHNlLCJNeVNRTCBjb21wYXRpYmxlIjpmYWxzZSwicm93LW9yaWVudGVkIjpmYWxzZSwiQ2xpY2tIb3VzZSBkZXJpdmF0aXZlIjpmYWxzZSwiZW1iZWRkZWQiOmZhbHNlLCJzZXJ2ZXJsZXNzIjpmYWxzZSwiZGF0YWZyYW1lIjp0cnVlLCJhd3MiOmZhbHNlLCJhenVyZSI6ZmFsc2UsImFuYWx5dGljYWwiOmZhbHNlLCJSdXN0IjpmYWxzZSwic2VhcmNoIjpmYWxzZSwiZG9jdW1lbnQiOmZhbHNlLCJzb21ld2hhdCBQb3N0Z3JlU1FMIGNvbXBhdGlibGUiOmZhbHNlLCJ0aW1lLXNlcmllcyI6ZmFsc2V9LCJtYWNoaW5lIjp7IjE2IHZDUFUgMTI4R0IiOnRydWUsIjggdkNQVSA2NEdCIjp0cnVlLCJzZXJ2ZXJsZXNzIjp0cnVlLCIxNmFjdSI6dHJ1ZSwiYzZhLjR4bGFyZ2UsIDUwMGdiIGdwMiI6dHJ1ZSwiTCI6dHJ1ZSwiTSI6dHJ1ZSwiUyI6dHJ1ZSwiWFMiOnRydWUsImM2YS5tZXRhbCwgNTAwZ2IgZ3AyIjp0cnVlLCIxOTJHQiI6dHJ1ZSwiMjRHQiI6dHJ1ZSwiMzYwR0IiOnRydWUsIjQ4R0IiOnRydWUsIjcyMEdCIjp0cnVlLCI5NkdCIjp0cnVlLCJkZXYiOnRydWUsIjcwOEdCIjp0cnVlLCJjNW4uNHhsYXJnZSwgNTAwZ2IgZ3AyIjp0cnVlLCJBbmFseXRpY3MtMjU2R0IgKDY0IHZDb3JlcywgMjU2IEdCKSI6dHJ1ZSwiYzUuNHhsYXJnZSwgNTAwZ2IgZ3AyIjp0cnVlLCJjNmEuNHhsYXJnZSwgMTUwMGdiIGdwMiI6dHJ1ZSwiY2xvdWQiOnRydWUsImRjMi44eGxhcmdlIjp0cnVlLCJyYTMuMTZ4bGFyZ2UiOnRydWUsInJhMy40eGxhcmdlIjp0cnVlLCJyYTMueGxwbHVzIjp0cnVlLCJTMiI6dHJ1ZSwiUzI0Ijp0cnVlLCIyWEwiOnRydWUsIjNYTCI6dHJ1ZSwiNFhMIjp0cnVlLCJYTCI6dHJ1ZSwiTDEgLSAxNkNQVSAzMkdCIjp0cnVlLCJjNmEuNHhsYXJnZSwgNTAwZ2IgZ3AzIjp0cnVlfSwiY2x1c3Rlcl9zaXplIjp7IjEiOnRydWUsIjIiOnRydWUsIjQiOnRydWUsIjgiOnRydWUsIjE2Ijp0cnVlLCIzMiI6dHJ1ZSwiNjQiOnRydWUsIjEyOCI6dHJ1ZSwic2VydmVybGVzcyI6dHJ1ZX0sIm1ldHJpYyI6ImhvdCIsInF1ZXJpZXMiOlt0cnVlLHRydWUsdHJ1ZSx0cnVlLHRydWUsdHJ1ZSx0cnVlLHRydWUsdHJ1ZSx0cnVlLHRydWUsdHJ1ZSx0cnVlLHRydWUsdHJ1ZSx0cnVlLHRydWUsdHJ1ZSx0cnVlLHRydWUsdHJ1ZSx0cnVlLHRydWUsdHJ1ZSx0cnVlLHRydWUsdHJ1ZSx0cnVlLHRydWUsdHJ1ZSx0cnVlLHRydWUsdHJ1ZSx0cnVlLHRydWUsdHJ1ZSx0cnVlLHRydWUsdHJ1ZSx0cnVlLHRydWUsdHJ1ZSx0cnVlXX0=)
322
+
323
+
315
324
  <div align="center">
316
- <img src="https://github.com/chdb-io/chdb/raw/main/docs/_static/chdb-vs-pandas.jpg" width="800">
325
+ <img src="https://github.com/chdb-io/chdb/raw/main/docs/_static/df_bench.png" width="800">
317
326
  </div>
318
327
 
319
328
 
@@ -337,6 +346,10 @@ There are something you can help:
337
346
 
338
347
  We welcome bindings for other languages, please refer to [bindings](bindings.md) for more details.
339
348
 
349
+ ## Paper
350
+
351
+ - [ClickHouse - Lightning Fast Analytics for Everyone](https://www.vldb.org/pvldb/vol17/p3731-schulze.pdf)
352
+
340
353
  ## License
341
354
  Apache 2.0, see [LICENSE](LICENSE.txt) for more information.
342
355
 
@@ -1,25 +1,28 @@
1
- chdb/__init__.py,sha256=WoHYwRMh_xAz1NY8zkt2Nv0ZedTgUjQeR6Rivppk7DE,2923
1
+ chdb/__init__.py,sha256=li1sCjhLx7HmRBbWtekahee1iQtJl5p69oGYIaVVplk,2994
2
2
  chdb/__main__.py,sha256=xNNtDY38d973YM5dlxiIazcqqKhXJSpNb7JflyyrXGE,1185
3
- chdb/_chdb.cpython-39-darwin.so,sha256=PZFJTUVbZfcv9e5GFCH0WqU4I_b6bYIT5sc8VFVkWQ4,355968448
3
+ chdb/_chdb.cpython-39-darwin.so,sha256=58Nvy86wQxr1k998QjxHvLV0FvrAaPdRiAAfkthoYpo,356021376
4
4
  chdb/rwabc.py,sha256=tbiwCrXirfrfx46wCJxS64yvFe6pVWIPGdSuvrAL5Ys,2102
5
5
  chdb/dataframe/__init__.py,sha256=1_mrZZiJwqBTnH_P8_FCbbYXIWWY5sxnaFpe3-tDLF4,680
6
6
  chdb/dataframe/query.py,sha256=ggvE8A5vtabFg9gSTp99S7LCrnIEwbWtb-PtJVT8Ct0,12759
7
7
  chdb/dbapi/__init__.py,sha256=aaNhxXNBC1ZkFr260cbGR8msOinTp0VoNTT_j8AXGUc,2205
8
- chdb/dbapi/connections.py,sha256=P4ZKot4r9UPC8dLfVRV4qFTZ4_VXIQdF84jjZ-01fjo,6213
8
+ chdb/dbapi/connections.py,sha256=4RBO0h-B149xEicE8cXSSJl9wpXa4FQMY_4SghgEvCw,2762
9
9
  chdb/dbapi/converters.py,sha256=qS9k0Kzo_vDQxnFtsJ_3pLjlTrBK09SfdWcXdxzIrtI,7413
10
- chdb/dbapi/cursors.py,sha256=kpMam9rZH80z5Td3DbIusJPqPr_gRqyKVA57BI9PNac,9354
10
+ chdb/dbapi/cursors.py,sha256=OXF36raoyI3MIC5SCQ5IvnCtbOnppga4Q1IKOt2EIsk,7920
11
11
  chdb/dbapi/err.py,sha256=kUI9-A8LNqBoMoo4jh2NFsLCOLoPEwh9YIuz_qMoLoM,2017
12
12
  chdb/dbapi/times.py,sha256=_qXgDaYwsHntvpIKSKXp1rrYIgtq6Z9pLyLnO2XNoL0,360
13
13
  chdb/dbapi/constants/FIELD_TYPE.py,sha256=ytFzgAnGmb9hvdsBlnK68qdZv_a6jYFIXT6VSAb60z8,370
14
14
  chdb/dbapi/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  chdb/session/__init__.py,sha256=fCUROZ5L1-92o2lcASiWJpFu-80-kDoSrNfouLEmLg8,50
16
16
  chdb/session/state.py,sha256=Hc9lJGLZ97Un7ElBbeWeCZoc9p_m1k_ojQbzJMPBPKg,1341
17
+ chdb/state/__init__.py,sha256=RVUIWDqDi7gte4Os7Mz1wPXFyFpdHT_p1klJC7QtluI,55
18
+ chdb/state/sqlitelike.py,sha256=RSVJW5M3hH_TUBcXiH7zna21WTSGbqkOccyEiFFd8PI,4425
17
19
  chdb/udf/__init__.py,sha256=qSMaPEre7w1pYz8uJ-iZtuu8wYOUNRcI_8UNuaOymGE,80
18
20
  chdb/udf/udf.py,sha256=z0A1RmyZrx55bykpvvS-LpVt1lMrQOexjvU5zxCdCSA,3935
19
- chdb/utils/__init__.py,sha256=bC1m0_bbbl4Eqis0t_qt5s7hySriU7vsO1_sx7JFbGU,158
21
+ chdb/utils/__init__.py,sha256=tXRcwBRGW2YQNBZWV4Mitw5QlCu_qlSRCjllw15XHbs,171
22
+ chdb/utils/trace.py,sha256=W-pvDoKlnzq6H_7FiWjr5_teN40UNE4E5--zbUrjOIc,2511
20
23
  chdb/utils/types.py,sha256=MGLFIjoDvu7Uc2Wy8EDY60jjue66HmMPxbhrujjrZxQ,7530
21
- chdb-2.1.1.dist-info/LICENSE.txt,sha256=isYVtNCO5910aj6e9bJJ6kQceivkLqsMlFSNYwzGGKI,11366
22
- chdb-2.1.1.dist-info/METADATA,sha256=l0tJPTzemw1dtkkOy7XBMjuw2THbdcgDW4V2mgJ-2t4,14944
23
- chdb-2.1.1.dist-info/WHEEL,sha256=qPSLW7RKkSazFLr9GqnCq82B2cy1OopxhNJnY83fx9k,107
24
- chdb-2.1.1.dist-info/top_level.txt,sha256=se0Jj0A2-ijfMW51hIjiuNyDJPqy5xJU1G8a_IEdllI,11
25
- chdb-2.1.1.dist-info/RECORD,,
24
+ chdb-2.2.0b1.dist-info/LICENSE.txt,sha256=isYVtNCO5910aj6e9bJJ6kQceivkLqsMlFSNYwzGGKI,11366
25
+ chdb-2.2.0b1.dist-info/METADATA,sha256=cjvQmdOiS1pll_E2ohhXF2DVb7wdbVndY00xLuCnVi8,19444
26
+ chdb-2.2.0b1.dist-info/WHEEL,sha256=bpdNn3EW1Ro7rhzacUi9J-tn-B2haoCtXthtgNlZT0g,107
27
+ chdb-2.2.0b1.dist-info/top_level.txt,sha256=se0Jj0A2-ijfMW51hIjiuNyDJPqy5xJU1G8a_IEdllI,11
28
+ chdb-2.2.0b1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.2.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp39-cp39-macosx_11_0_arm64
5
5