Skip to content
This repository was archived by the owner on Oct 29, 2024. It is now read-only.

Parameter binding for client's query() method #678

Merged
merged 4 commits into from
Mar 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]

### Added
- query() now accepts a bind_params argument for parameter binding (#678 thx @clslgrnc)

### Changed

Expand Down
9 changes: 8 additions & 1 deletion examples/tutorial.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ def main(host='localhost', port=8086):
dbname = 'example'
dbuser = 'smly'
dbuser_password = 'my_secret_password'
query = 'select value from cpu_load_short;'
query = 'select Float_value from cpu_load_short;'
query_where = 'select Int_value from cpu_load_short where host=$host;'
bind_params = {'host': 'server01'}
json_body = [
{
"measurement": "cpu_load_short",
Expand Down Expand Up @@ -50,6 +52,11 @@ def main(host='localhost', port=8086):

print("Result: {0}".format(result))

print("Querying data: " + query_where)
result = client.query(query_where, bind_params=bind_params)

print("Result: {0}".format(result))

print("Switch user: " + user)
client.switch_user(user, password)

Expand Down
12 changes: 12 additions & 0 deletions influxdb/_dataframe_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ def write_points(self,
def query(self,
query,
params=None,
bind_params=None,
epoch=None,
expected_response_code=200,
database=None,
Expand All @@ -153,8 +154,18 @@ def query(self,
"""
Query data into a DataFrame.

.. danger::
In order to avoid injection vulnerabilities (similar to `SQL
injection <https://www.owasp.org/index.php/SQL_Injection>`_
vulnerabilities), do not directly include untrusted data into the
``query`` parameter, use ``bind_params`` instead.

:param query: the actual query string
:param params: additional parameters for the request, defaults to {}
:param bind_params: bind parameters for the query:
any variable in the query written as ``'$var_name'`` will be
replaced with ``bind_params['var_name']``. Only works in the
``WHERE`` clause and takes precedence over ``params['params']``
:param epoch: response timestamps to be in epoch format either 'h',
'm', 's', 'ms', 'u', or 'ns',defaults to `None` which is
RFC3339 UTC format with nanosecond precision
Expand All @@ -172,6 +183,7 @@ def query(self,
:rtype: :class:`~.ResultSet`
"""
query_args = dict(params=params,
bind_params=bind_params,
epoch=epoch,
expected_response_code=expected_response_code,
raise_errors=raise_errors,
Expand Down
18 changes: 18 additions & 0 deletions influxdb/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ def _read_chunked_response(response, raise_errors=True):
def query(self,
query,
params=None,
bind_params=None,
epoch=None,
expected_response_code=200,
database=None,
Expand All @@ -354,13 +355,25 @@ def query(self,
method="GET"):
"""Send a query to InfluxDB.

.. danger::
In order to avoid injection vulnerabilities (similar to `SQL
injection <https://www.owasp.org/index.php/SQL_Injection>`_
vulnerabilities), do not directly include untrusted data into the
``query`` parameter, use ``bind_params`` instead.

:param query: the actual query string
:type query: str

:param params: additional parameters for the request,
defaults to {}
:type params: dict

:param bind_params: bind parameters for the query:
any variable in the query written as ``'$var_name'`` will be
replaced with ``bind_params['var_name']``. Only works in the
``WHERE`` clause and takes precedence over ``params['params']``
:type bind_params: dict

:param epoch: response timestamps to be in epoch format either 'h',
'm', 's', 'ms', 'u', or 'ns',defaults to `None` which is
RFC3339 UTC format with nanosecond precision
Expand Down Expand Up @@ -394,6 +407,11 @@ def query(self,
if params is None:
params = {}

if bind_params is not None:
params_dict = json.loads(params.get('params', '{}'))
params_dict.update(bind_params)
params['params'] = json.dumps(params_dict)

params['q'] = query
params['db'] = database or self._database

Expand Down
7 changes: 4 additions & 3 deletions influxdb/tests/dataframe_client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -884,10 +884,11 @@ def test_multiquery_into_dataframe(self):
expected = [{'cpu_load_short': pd1}, {'cpu_load_short': pd2}]

cli = DataFrameClient('host', 8086, 'username', 'password', 'db')
iql = "SELECT value FROM cpu_load_short WHERE region='us-west';"\
"SELECT count(value) FROM cpu_load_short WHERE region='us-west'"
iql = "SELECT value FROM cpu_load_short WHERE region=$region;"\
"SELECT count(value) FROM cpu_load_short WHERE region=$region"
bind_params = {'region': 'us-west'}
with _mocked_session(cli, 'GET', 200, data):
result = cli.query(iql)
result = cli.query(iql, bind_params=bind_params)
for r, e in zip(result, expected):
for k in e:
assert_frame_equal(e[k], r[k])
Expand Down
4 changes: 3 additions & 1 deletion influxdb/tests/server_tests/client_test_with_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,9 @@ def test_write_points_batch(self):
batch_size=2)
time.sleep(5)
net_in = self.cli.query("SELECT value FROM network "
"WHERE direction='in'").raw
"WHERE direction=$dir",
bind_params={'dir': 'in'}
).raw
net_out = self.cli.query("SELECT value FROM network "
"WHERE direction='out'").raw
cpu = self.cli.query("SELECT value FROM cpu_usage").raw
Expand Down