Skip to content

Commit

Permalink
Turn off server-side parameters substitution by default #376 #410
Browse files Browse the repository at this point in the history
  • Loading branch information
xzkostyan committed Jan 19, 2024
1 parent 092bb9b commit 24e8b35
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 5 deletions.
15 changes: 14 additions & 1 deletion clickhouse_driver/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ class Client(object):
tuple set ``namedtuple_as_json`` to ``False``.
Default: True.
New in version *0.2.6*.
* ``server_side_params`` -- Species on which side query parameters
should be rendered into placeholders.
Default: False. Means that parameters are rendered
on driver's side.
New in version *0.2.7*.
"""

available_client_settings = (
Expand All @@ -74,7 +79,8 @@ class Client(object):
'opentelemetry_tracestate',
'quota_key',
'input_format_null_as_default',
'namedtuple_as_json'
'namedtuple_as_json',
'server_side_params'
)

def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -107,6 +113,9 @@ def __init__(self, *args, **kwargs):
),
'namedtuple_as_json': self.settings.pop(
'namedtuple_as_json', True
),
'server_side_params': self.settings.pop(
'server_side_params', False
)
}

Expand Down Expand Up @@ -766,6 +775,10 @@ def substitute_params(self, query, params, context):
# prints: SELECT 1234, 'bar'
print(substituted_query)
"""
# In case of server side templating we don't substitute here.
if self.connection.context.client_settings['server_side_params']:
return query

if not isinstance(params, dict):
raise ValueError('Parameters are expected in dict form')

Expand Down
11 changes: 7 additions & 4 deletions clickhouse_driver/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,10 +714,13 @@ def send_query(self, query, query_id=None, params=None):
write_binary_str(query, self.fout)

if revision >= defines.DBMS_MIN_PROTOCOL_VERSION_WITH_PARAMETERS:
# Always settings_as_strings = True
escaped = escape_params(
params or {}, self.context, for_server=True
)
if self.context.client_settings['server_side_params']:
# Always settings_as_strings = True
escaped = escape_params(
params or {}, self.context, for_server=True
)
else:
escaped = {}
write_settings(escaped, self.fout, True, SettingsFlags.CUSTOM)

logger.debug('Query: %s', query)
Expand Down
10 changes: 10 additions & 0 deletions tests/test_substitution.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ def test_substitute_object(self):
class ServerSideParametersSubstitutionTestCase(BaseTestCase):
required_server_version = (22, 8)

client_kwargs = {'settings': {'server_side_params': True}}

def test_int(self):
rv = self.client.execute('SELECT {x:Int32}', {'x': 123})
self.assertEqual(rv, [(123, )])
Expand All @@ -268,3 +270,11 @@ def test_escaped_str(self):
'SELECT {x:String}, length({x:String})', {'x': "'"}
)
self.assertEqual(rv, [("'", 1)])


class NoServerSideParametersSubstitutionTestCase(BaseTestCase):
def test_reserved_keywords(self):
self.client.execute(
'SELECT * FROM system.events LIMIT %(limit)s OFFSET %(offset)s',
{'limit': 20, 'offset': 30}
)

0 comments on commit 24e8b35

Please sign in to comment.