2

I'm writing some scripts in Python 3 and need to read data from PostgreSQL database. In a scenario, I need to append a query to another query and also prevent SQL Injection. I tried different ways (format string, sql.SQL) but couldn't success. Below is the code I used:

data = {
    'id': 14, 
    'user_id': (16, 'Administrator'), 
    'from_date': datetime.datetime(2019, 1, 6, 5, 45, 15), 
    'to_date': datetime.datetime(2020, 5, 3, 5, 45, 15), 
    'state': 'to_be_review', 
    'language': 'english',
}
query = " AND hr.unit_identification_code_id IN (%s)" % ','.join(map(str, uic_ids))
user_id = data.get('user_id') and data.get('user_id')[0] or 0
    if uic_ids:
        if data.get('state') != 'reject_submit':
            self._cr.execute(sql.SQL("""
                                    SELECT
                                        elh.id 
                                    FROM
                                        employee_log_history elh 
                                        LEFT JOIN
                                            hr_employee hr 
                                            ON elh.hr_employee_id = hr.id 
                                    WHERE
                                        elh.create_date >= %s 
                                        AND elh.create_date <=%s 
                                        AND elh.create_uid = %s 
                                        AND elh.state = %s {}
                                    """).format(sql.Identifier(query)), (data.get('from_date'), data.get('to_date'), user_id, data.get('state'),)  
                                )

but when I run the script as the result I get the first query in double quotes and bellow error:

psycopg2.ProgrammingError: syntax error at or near "" AND hr.unit_identification_code_id IN (33,34,35,36,37,38,39,40,41,42,49,47,60,61,63,64,62,43,46,58,59,50,57,48,44,51,52)""
LINE 13: ...                   AND elh.state = 'to_be_review' " AND hr.u...

Now I wonder if Is it possible to execute this query and prevent SQL Injection and also append first query in second one?

1 Answer 1

2

sql.Identifier is for quoting identifiers such as table names or column names, not for adapting chunks of SQL. To do what you want, use sql.SQL.

Here's a simplified example, adding an additional condition to a WHERE clause:

import psycopg2
from psycopg2 import sql

conn = psycopg2.connect(database='test')
cur = conn.cursor()

stmt1 = "SELECT name, password FROM users WHERE name = %s"
stmt2 = ' AND {} = %s'

composed = sql.SQL(stmt2).format('password')

print(composed)
Composed([SQL(' AND '), Identifier('password'), SQL(' = %s')])

print(repr(composed.as_string(cur)))
' AND "password" = %s'

cur.mogrify(stmt1 + composed.as_string(cur), ('Alice', 'secret'))
b'SELECT name, password FROM users WHERE name = \'Alice\' AND "password" = \'secret\''

Mixing strings (stmt) and objects (composed) feels a bit hacky. You might want to use objects consistently in your code, but that's up to you.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.