ClickHouse / clickhouse-connect

Python driver/sqlalchemy/superset connectors
Apache License 2.0
332 stars 65 forks source link

Cannot create client for read-only remote host if local client is created first #426

Closed glennmoy closed 4 days ago

glennmoy commented 5 days ago

Describe the bug

Creating a client connecting to a read-only remote host (e.g. play.clickhouse.com) errors if one has already created a client connecting to localhost:

 Code: 164. DB::Exception: Cannot modify 'date_time_input_format' setting in readonly mode. (READONLY) (version 24.11.1.548 (official build))

Steps to reproduce

  1. Create a local client
  2. Create a remote client connecting to a read-only host (e.g. play.clickhouse.com)

Expected behaviour

I expect to be able to create a client for any host regardless if another client was created first.

Code example

MWE:

>>> import clickhouse_connect
>>> client_local = clickhouse_connect.get_client()
>>> client_remote = clickhouse_connect.get_client(host="play.clickhouse.com", user="explorer", secure=True, port=443)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/glenn.moynihan/gitlab/testenv/lib/python3.9/site-packages/clickhouse_connect/driver/__init__.py", line 115, in create_client
    return HttpClient(interface, host, port, username, password, database, settings=settings, **kwargs)
  File "/Users/glenn.moynihan/gitlab/testenv/lib/python3.9/site-packages/clickhouse_connect/driver/httpclient.py", line 156, in __init__
    super().__init__(database=database,
  File "/Users/glenn.moynihan/gitlab/testenv/lib/python3.9/site-packages/clickhouse_connect/driver/client.py", line 68, in __init__
    self._init_common_settings(apply_server_timezone)
  File "/Users/glenn.moynihan/gitlab/testenv/lib/python3.9/site-packages/clickhouse_connect/driver/client.py", line 73, in _init_common_settings
    tuple(self.command('SELECT version(), timezone()', use_database=False))
  File "/Users/glenn.moynihan/gitlab/testenv/lib/python3.9/site-packages/clickhouse_connect/driver/httpclient.py", line 350, in command
    response = self._raw_request(payload, params, headers, method, fields=fields, server_wait=False)
  File "/Users/glenn.moynihan/gitlab/testenv/lib/python3.9/site-packages/clickhouse_connect/driver/httpclient.py", line 461, in _raw_request
    self._error_handler(response)
  File "/Users/glenn.moynihan/gitlab/testenv/lib/python3.9/site-packages/clickhouse_connect/driver/httpclient.py", line 384, in _error_handler
    raise OperationalError(err_str) if retried else DatabaseError(err_str) from None
clickhouse_connect.driver.exceptions.DatabaseError: HTTPDriver for https://play.clickhouse.com:443 received ClickHouse error code 164
 Code: 164. DB::Exception: Cannot modify 'date_time_input_format' setting in readonly mode. (READONLY) (version 24.11.1.548 (official build))

Reversing the order of the clients works

>>> import clickhouse_connect
>>> client_remote = clickhouse_connect.get_client(host="play.clickhouse.com", user="explorer", secure=True, port=443)
>>> client_local = clickhouse_connect.get_client()

Some debugging revealed that a query parameter is being added to the HTTP url for the remote client if it's called after first creating a local client: https://play.clickhouse.com:443?date_time_input_format=best_effort.

This seems to come from the params of the HTTPClient itself, which are being persisted between calls to get_client. i.e. date_time_input_format is being set by the local client and inherited by the remote client, which doesn't seem like it should be happening:

>>> import clickhouse_connect
>>> client = clickhouse_connect.get_client()
>>> client.params
{'date_time_input_format': 'best_effort', 'session_id': 'a217756f-8c97-4ada-aee3-451cf9d902ca'}
>>>
>>> clickhouse_connect.get_client(host="play.clickhouse.com", user="explorer", secure=True, port=443)
...
...
Code: 164. DB::Exception: Cannot modify 'date_time_input_format' setting in readonly mode. (READONLY) (version 24.11.1.548 (official build))

Configuration

Environment

genzgd commented 5 days ago

Thanks for the detailed report. I agree that such inheritance should not be happening, so I'm guessing that the original class level dictionary is being incorrectly modified.