-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue #32 - escape table and field names
Quite often, there is a need to create dynamic queries where the table or column name is only known at run time. Until now, one had to resort to the potentially dangerous | sqlsafe filter and had to ensure that the table / column name did not have any sql injection. Most databases provide a way to quote identifiers. Most databases uses double quotes as a way to quote table / column names. Notable exception is MySql, which by default uses backticks as the escape character With this commit, we add a new jinja2 filter call identifier. This filter will automatically quote and escape the table/column names that are injected at run time. Typical usage: template = 'SELECT {{colname|identifier}} FROM {{tablename|identifier}}' will generate a query like 'SELECT somecol FROM myschema.sometable
- Loading branch information
1 parent
fb58b0e
commit d7fdc7a
Showing
5 changed files
with
122 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
from testcontainers.postgres import PostgresContainer | ||
from testcontainers.mysql import MySqlContainer | ||
import sqlalchemy | ||
import unittest | ||
from jinjasql import JinjaSql | ||
|
||
class PostgresTest(unittest.TestCase): | ||
|
||
# Core idea inspired from | ||
# https://stackoverflow.com/questions/8416208/in-python-is-there-a-good-idiom-for-using-context-managers-in-setup-teardown | ||
# | ||
# Override the run method to automatically | ||
# a. launch a postgres docker container | ||
# b. create a sqlalchemy connection | ||
# c. at the end of the test, kill the docker container | ||
def run(self, result=None): | ||
with PostgresContainer("postgres:9.5") as postgres: | ||
self.engine = sqlalchemy.create_engine(postgres.get_connection_url()) | ||
super(PostgresTest, self).run(result) | ||
|
||
def test_bind_array(self): | ||
'It should be possible to bind arrays in a query' | ||
j = JinjaSql() | ||
data = { | ||
"some_num": 1, | ||
"some_array": [1,2,3] | ||
} | ||
template = """ | ||
SELECT {{some_num}} = ANY({{some_array}}) | ||
""" | ||
query, params = j.prepare_query(template, data) | ||
result = self.engine.execute(query, params).fetchone() | ||
self.assertTrue(result[0]) | ||
|
||
def test_quoted_tables(self): | ||
j = JinjaSql() | ||
data = { | ||
"all_tables": ("information_schema", "tables") | ||
} | ||
template = """ | ||
select table_name from {{all_tables|identifier}} | ||
where table_name = 'pg_user' | ||
""" | ||
query, params = j.prepare_query(template, data) | ||
result = self.engine.execute(query, params).fetchall() | ||
self.assertEqual(len(result), 1) | ||
|
||
class MySqlTest(unittest.TestCase): | ||
def run(self, result=None): | ||
with MySqlContainer("mysql:5.7.17") as mysql: | ||
self.engine = sqlalchemy.create_engine(mysql.get_connection_url()) | ||
super(MySqlTest, self).run(result) | ||
|
||
def test_quoted_tables(self): | ||
j = JinjaSql(identifier_quote_character='`') | ||
data = { | ||
"all_tables": ("information_schema", "tables") | ||
} | ||
template = """ | ||
select table_name from {{all_tables|identifier}} | ||
where table_name = 'SESSION_STATUS' | ||
""" | ||
query, params = j.prepare_query(template, data) | ||
result = self.engine.execute(query, params).fetchall() | ||
self.assertEqual(len(result), 1) | ||
|
||
if __name__ == '__main__': | ||
unittest.main() |