mymarilyn / clickhouse-driver

ClickHouse Python Driver with native interface support
https://clickhouse-driver.readthedocs.io
Other
1.21k stars 212 forks source link

client.execute return [] insted of no of row insterted #221

Open hackaholic opened 3 years ago

hackaholic commented 3 years ago

Describe the bug client.execute return [] instead of no of row insterted

To Reproduce clickhouse-client

clickhouse :) CREATE TABLE IF NOT EXISTS test.pets (id UInt32, name String, eventTime DateTime) Engine = ReplicatedMergeTree('/clickhouse/iond_shard/test/tables/dd_pets/{shard}', '{replica}') PARTITION BY toYYYYMMDD(eventTime) ORDER BY (id,eventTime)

CREATE TABLE IF NOT EXISTS test.pets
(
    `id` UInt32,
    `name` String,
    `eventTime` DateTime
)
ENGINE = ReplicatedMergeTree('/clickhouse/iond_shard/test/tables/dd_pets/{shard}', '{replica}')
PARTITION BY toYYYYMMDD(eventTime)
ORDER BY (id, eventTime)

Query id: 12396bd5-4802-47d7-9acf-494a20bc9f81

Ok.

0 rows in set. Elapsed: 0.058 sec.

using python clickhouse driver to insert data:

>>> from clickhouse_driver import Client
>>> clickhouse_driver.VERSION
(0, 2, 0)
>>> c = Client("10.10.10.168")
>>> c.execute("show databases")
[('default',), ('system',), ('test',)]
>>> c.execute("insert into test.pets (id, name, eventTime) values(1, 'coco', '2021-04-26 18:07:13')")
[]
 >>> c.execute("select * from test.pets")
[(1, 'coco', datetime.datetime(2021, 4, 26, 18, 7, 13))]

Expected behavior It should return no of inserted row from the Doc: https://readthedocs.org/projects/clickhouse-driver/downloads/pdf/latest/

Versions

hackaholic commented 3 years ago

I can see in source code function are returning the inserted_rows ` def execute(self, query, params=None, with_column_types=False, external_tables=None, query_id=None, settings=None, types_check=False, columnar=False): """ Executes query.

    Establishes new connection if it wasn't established yet.
    After query execution connection remains intact for next queries.
    If connection can't be reused it will be closed and new connection will
    be created.

    :param query: query that will be send to server.
    :param params: substitution parameters for SELECT queries and data for
                   INSERT queries. Data for INSERT can be `list`, `tuple`
                   or :data:`~types.GeneratorType`.
                   Defaults to ``None`` (no parameters  or data).
    :param with_column_types: if specified column names and types will be
                              returned alongside with result.
                              Defaults to ``False``.
    :param external_tables: external tables to send.
                            Defaults to ``None`` (no external tables).
    :param query_id: the query identifier. If no query id specified
                     ClickHouse server will generate it.
    :param settings: dictionary of query settings.
                     Defaults to ``None`` (no additional settings).
    :param types_check: enables type checking of data for INSERT queries.
                        Causes additional overhead. Defaults to ``False``.
    :param columnar: if specified the result of the SELECT query will be
                     returned in column-oriented form.
                     It also allows to INSERT data in columnar form.
                     Defaults to ``False`` (row-like form).

    :return: * number of inserted rows for INSERT queries with data.
               Returning rows count from INSERT FROM SELECT is not
               supported.
             * if `with_column_types=False`: `list` of `tuples` with
               rows/columns.
             * if `with_column_types=True`: `tuple` of 2 elements:
                * The first element is `list` of `tuples` with
                  rows/columns.
                * The second element information is about columns: names
                  and types.
    """

    start_time = time()
    self.make_query_settings(settings)
    self.connection.force_connect()
    self.last_query = QueryInfo()

    try:
        # INSERT queries can use list/tuple/generator of list/tuples/dicts.
        # For SELECT parameters can be passed in only in dict right now.
        is_insert = isinstance(params, (list, tuple, types.GeneratorType))

        if is_insert:
            rv = self.process_insert_query(
                query, params, external_tables=external_tables,
                query_id=query_id, types_check=types_check,
                columnar=columnar
            )
        else:
            rv = self.process_ordinary_query(
                query, params=params, with_column_types=with_column_types,
                external_tables=external_tables,
                query_id=query_id, types_check=types_check,
                columnar=columnar
            )
        self.last_query.store_elapsed(time() - start_time)
        return rv

    except (Exception, KeyboardInterrupt):
        self.disconnect()
        raise

def process_insert_query(self, query_without_data, data,
                         external_tables=None, query_id=None,
                         types_check=False, columnar=False):
    self.connection.send_query(query_without_data, query_id=query_id)
    self.connection.send_external_tables(external_tables,
                                         types_check=types_check)

    sample_block = self.receive_sample_block()
    if sample_block:
        rv = self.send_data(sample_block, data,
                            types_check=types_check, columnar=columnar)
        self.receive_end_of_query()
        return rv

`

hackaholic commented 3 years ago

I am able to get no of row Inserted if array of values is passed as separate argument.

c.execute("insert into test.pets (id, name, eventTime) VALUES", [(2, 'Mini', datetime.datetime.now()), (3, 'Dora', datetime.datetime.now())]) 2

But if we pass complete query as string, then we have issue