Browse Source

Update contrib/python/clickhouse-connect to 0.6.19

robot-contrib 1 year ago
parent
commit
c08cc1568c

+ 1 - 1
contrib/python/clickhouse-connect/.dist-info/METADATA

@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: clickhouse-connect
-Version: 0.6.18
+Version: 0.6.19
 Summary: ClickHouse Database Core Driver for Python, Pandas, and Superset
 Home-page: https://github.com/ClickHouse/clickhouse-connect
 Author: ClickHouse Inc.

+ 1 - 1
contrib/python/clickhouse-connect/clickhouse_connect/__version__.py

@@ -1 +1 @@
-version = '0.6.18'
+version = '0.6.19'

+ 1 - 0
contrib/python/clickhouse-connect/clickhouse_connect/cc_sqlalchemy/dialect.py

@@ -22,6 +22,7 @@ class ClickHouseDialect(DefaultDialect):
     default_schema_name = 'default'
     supports_native_decimal = True
     supports_native_boolean = True
+    supports_statement_cache = False
     returns_unicode_strings = True
     postfetch_lastrowid = False
     ddl_compiler = ChDDLCompiler

+ 2 - 2
contrib/python/clickhouse-connect/clickhouse_connect/datatypes/container.py

@@ -3,7 +3,7 @@ import logging
 from typing import Sequence, Collection
 
 from clickhouse_connect.driver.insert import InsertContext
-from clickhouse_connect.driver.query import QueryContext
+from clickhouse_connect.driver.query import QueryContext, quote_identifier
 from clickhouse_connect.driver.types import ByteSource
 from clickhouse_connect.json_impl import any_to_json
 from clickhouse_connect.datatypes.base import ClickHouseType, TypeDef
@@ -94,7 +94,7 @@ class Tuple(ClickHouseType):
         self.element_names = type_def.keys
         self.element_types = [get_from_name(name) for name in type_def.values]
         if self.element_names:
-            self._name_suffix = f"({', '.join(k + ' ' + str(v) for k, v in zip(type_def.keys, type_def.values))})"
+            self._name_suffix = f"({', '.join(quote_identifier(k) + ' ' + str(v) for k, v in zip(type_def.keys, type_def.values))})"
         else:
             self._name_suffix = type_def.arg_str
 

+ 2 - 2
contrib/python/clickhouse-connect/clickhouse_connect/datatypes/string.py

@@ -45,9 +45,9 @@ class String(ClickHouseType):
     # pylint: disable=duplicate-code,too-many-nested-blocks,too-many-branches
     def _write_column_binary(self, column: Union[Sequence, MutableSequence], dest: bytearray, ctx: InsertContext):
         encoding = None
-        if isinstance(self._first_value(column), str):
+        if not isinstance(self._first_value(column), bytes):
             encoding = ctx.encoding or self.encoding
-        data_conv.write_str_col(column, encoding, dest)
+        data_conv.write_str_col(column, self.nullable, encoding, dest)
 
     def _active_null(self, ctx):
         if ctx.use_none:

+ 6 - 1
contrib/python/clickhouse-connect/clickhouse_connect/driver/dataconv.py

@@ -5,6 +5,7 @@ from typing import Sequence, Optional, Any
 from uuid import UUID, SafeUUID
 
 from clickhouse_connect.driver.common import int_size
+from clickhouse_connect.driver.exceptions import DataError
 from clickhouse_connect.driver.types import ByteSource
 from clickhouse_connect.driver.options import np
 
@@ -110,14 +111,18 @@ def pivot(data: Sequence[Sequence], start_row: int, end_row: int) -> Sequence[Se
     return tuple(zip(*data[start_row: end_row]))
 
 
-def write_str_col(column: Sequence, encoding: Optional[str], dest: bytearray):
+def write_str_col(column: Sequence, nullable: bool, encoding: Optional[str], dest: bytearray):
     app = dest.append
     for x in column:
         if not x:
+            if not nullable and x is None:
+                raise DataError('Invalid None value in non-Nullable column')
             app(0)
         else:
             if encoding:
                 x = x.encode(encoding)
+            else:
+                x = b''
             sz = len(x)
             while True:
                 b = sz & 0x7f

+ 6 - 6
contrib/python/clickhouse-connect/clickhouse_connect/driver/parser.py

@@ -130,13 +130,13 @@ def parse_columns(expr: str):
     named = False
     level = 0
     label = ''
-    in_str = False
+    quote = None
     while True:
         char = expr[pos]
         pos += 1
-        if in_str:
-            if "'" == char:
-                in_str = False
+        if quote:
+            if char == quote:
+                quote = None
             elif char == '\\' and expr[pos] == "'" and expr[pos:pos + 4] != "' = " and expr[pos:pos + 2] != "')":
                 label += expr[pos]
                 pos += 1
@@ -156,8 +156,8 @@ def parse_columns(expr: str):
                 elif char == ')':
                     columns.append(label)
                     break
-            if char == "'" and (not label or 'Enum' in label):
-                in_str = True
+            if char in ("'", '`') and (not label or 'Enum' in label):
+                quote = char
             elif char == '(':
                 level += 1
             elif char == ')':

+ 2 - 2
contrib/python/clickhouse-connect/clickhouse_connect/driver/query.py

@@ -341,7 +341,7 @@ class QueryResult(Closable):
 
 
 BS = '\\'
-must_escape = (BS, '\'')
+must_escape = (BS, '\'', '`')
 
 
 def quote_identifier(identifier: str):
@@ -349,7 +349,7 @@ def quote_identifier(identifier: str):
     if first_char in ('`', '"') and identifier[-1] == first_char:
         # Identifier is already quoted, assume that it's valid
         return identifier
-    return f'`{identifier}`'
+    return f'`{escape_str(identifier)}`'
 
 
 def finalize_query(query: str, parameters: Optional[Union[Sequence, Dict[str, Any]]],

+ 48 - 43
contrib/python/clickhouse-connect/clickhouse_connect/driverc/dataconv.pyx

@@ -19,6 +19,7 @@ from uuid import UUID, SafeUUID
 from libc.string cimport memcpy
 from datetime import tzinfo
 
+from clickhouse_connect.driver.exceptions import DataError
 
 @cython.boundscheck(False)
 @cython.wraparound(False)
@@ -254,7 +255,7 @@ cdef inline extend_byte_array(target: bytearray, int start, object source, Py_ss
 
 @cython.boundscheck(False)
 @cython.wraparound(False)
-def write_str_col(column: Sequence, encoding: Optional[str], dest: bytearray):
+def write_str_col(column: Sequence, nullable: bool, encoding: Optional[str], dest: bytearray):
     cdef unsigned long long buff_size = len(column) << 5
     cdef unsigned long long buff_loc = 0, sz = 0, dsz = 0
     cdef unsigned long long array_size = PyByteArray_GET_SIZE(dest)
@@ -263,50 +264,54 @@ def write_str_col(column: Sequence, encoding: Optional[str], dest: bytearray):
     cdef object encoded
     cdef char b
     cdef char * data
-    for x in column:
-        if not x:
-            temp_buff[buff_loc] = 0
-            buff_loc += 1
-            if buff_loc == buff_size:
-                extend_byte_array(dest, array_size, mv, buff_loc)
-                array_size += buff_loc
-                buff_loc = 0
-        else:
-            if not encoding:
-                data = x
-                dsz = len(x)
-            else:
-                encoded = x.encode(encoding)
-                dsz = len(encoded)
-                data = encoded
-            sz = dsz
-            while True:
-                b = sz & 0x7f
-                sz >>= 7
-                if sz != 0:
-                    b |= 0x80
-                temp_buff[buff_loc] = b
+    try:
+        for x in column:
+            if not x:
+                if not nullable and x is None:
+                    raise DataError('Invalid None value in non-Nullable column')
+                temp_buff[buff_loc] = 0
                 buff_loc += 1
                 if buff_loc == buff_size:
                     extend_byte_array(dest, array_size, mv, buff_loc)
                     array_size += buff_loc
                     buff_loc = 0
-                if sz == 0:
-                    break
-            if dsz + buff_loc >= buff_size:
-                if buff_loc > 0:  # Write what we have so far
-                    extend_byte_array(dest, array_size, mv, buff_loc)
-                    array_size += buff_loc
-                    buff_loc = 0
-                if (dsz << 4) > buff_size:  # resize our buffer for very large strings
-                    PyMem_Free(<void *> temp_buff)
-                    mv.release()
-                    buff_size = dsz << 6
-                    temp_buff = <char *> PyMem_Malloc(<size_t> buff_size)
-                    mv = PyMemoryView_FromMemory(temp_buff, buff_size, PyBUF_READ)
-            memcpy(temp_buff + buff_loc, data, dsz)
-            buff_loc += dsz
-    if buff_loc > 0:
-        extend_byte_array(dest, array_size, mv, buff_loc)
-    mv.release()
-    PyMem_Free(<void *>temp_buff)
+            else:
+                if not encoding:
+                    data = x
+                    dsz = len(x)
+                else:
+                    encoded = x.encode(encoding)
+                    dsz = len(encoded)
+                    data = encoded
+                sz = dsz
+                while True:
+                    b = sz & 0x7f
+                    sz >>= 7
+                    if sz != 0:
+                        b |= 0x80
+                    temp_buff[buff_loc] = b
+                    buff_loc += 1
+                    if buff_loc == buff_size:
+                        extend_byte_array(dest, array_size, mv, buff_loc)
+                        array_size += buff_loc
+                        buff_loc = 0
+                    if sz == 0:
+                        break
+                if dsz + buff_loc >= buff_size:
+                    if buff_loc > 0:  # Write what we have so far
+                        extend_byte_array(dest, array_size, mv, buff_loc)
+                        array_size += buff_loc
+                        buff_loc = 0
+                    if (dsz << 4) > buff_size:  # resize our buffer for very large strings
+                        PyMem_Free(<void *> temp_buff)
+                        mv.release()
+                        buff_size = dsz << 6
+                        temp_buff = <char *> PyMem_Malloc(<size_t> buff_size)
+                        mv = PyMemoryView_FromMemory(temp_buff, buff_size, PyBUF_READ)
+                memcpy(temp_buff + buff_loc, data, dsz)
+                buff_loc += dsz
+        if buff_loc > 0:
+            extend_byte_array(dest, array_size, mv, buff_loc)
+    finally:
+        mv.release()
+        PyMem_Free(<void *>temp_buff)

+ 3 - 3
contrib/python/clickhouse-connect/clickhouse_connect/tools/testing.py

@@ -1,7 +1,7 @@
 from typing import Sequence, Optional, Union, Dict, Any
 
 from clickhouse_connect.driver import Client
-from clickhouse_connect.driver.query import format_query_value
+from clickhouse_connect.driver.query import format_query_value, quote_identifier
 
 
 class TableContext:
@@ -29,14 +29,14 @@ class TableContext:
             self.column_names = columns
             self.column_types = column_types
         self.engine = engine
-        self.order_by = self.column_names[0] if order_by is None else order_by
+        self.order_by = quote_identifier(self.column_names[0]) if order_by is None else order_by
 
     def __enter__(self):
         if self.client.min_version('19'):
             self.client.command(f'DROP TABLE IF EXISTS {self.table}')
         else:
             self.client.command(f'DROP TABLE IF EXISTS {self.table} SYNC')
-        col_defs = ','.join(f'{name} {col_type}' for name, col_type in zip(self.column_names, self.column_types))
+        col_defs = ','.join(f'{quote_identifier(name)} {col_type}' for name, col_type in zip(self.column_names, self.column_types))
         create_cmd = f'CREATE TABLE {self.table} ({col_defs}) ENGINE {self.engine} ORDER BY {self.order_by}'
         if self.settings:
             create_cmd += ' SETTINGS '

Some files were not shown because too many files changed in this diff