How can a SQLAlchemy query use MySQL's REGEXP operator? - python

How can this SQL query:
SELECT * from table where field REGEXP 'apple|banna|pear';
be written using SQLAlchemy?
My base query looks like this:
query = session.query(TableFruitsDTO)

The SQLAlchemy docs describe how to use the MySQL REGEXP operator. Since there is no built-in function, use .op() to create the function:
query = session.query(TableFruitsDTO).filter(
TableFruitsDTO.field.op('regexp')(r'apple|banana|pear')
)

Related

SQLAlchemy: Dynamically pass schema and table name avoiding SQL Injection

How can I execute an SQL query where the schema and table name are passed in a function? Something like below?
def get(engine, schema: str, table: str):
query = text("select * from :schema.:table")
result = engine.connect().execute(query, schema=schema, table=table)
return result
Two things going on here:
Avoiding SQL injection
Dynamically setting a schema with (presumably) PostgreSQL
The first question has a very broad scope, you might want to look at older questions about SQLAlchemy and SQL Injection like this one SQLAlchemy + SQL Injection
Your second question can be addressed in a number of ways, though I would recommend the following approach from SQLAlchemy's documentation: https://docs.sqlalchemy.org/en/13/dialects/postgresql.html#remote-schema-table-introspection-and-postgresql-search-path
PostgreSQL supports a "search path" command which sets the schema for all operations in the transaction.
So your query code might look like:
qry_str = f"SET search_path TO {schema}";
Alternatively, if you use an SQLAlchemy declarative approach, you can use a MetaData object like in this question/answer SQLAlchemy support of Postgres Schemas
You could create a collection of existing table and schema names in your database and check inputs against those values before creating your query:
-- assumes we are connected to the correct *database*
SELECT table_schema, table_name
FROM information_schema.tables;

pyodbc execute SQL code

I am trying to use pyodbc cursor execute the right way to prevent injection attacks, as suggested here:
what does ? mean in python pyodbc module
My code is as follows:
query = """\
SELECT
?,count(*)
FROM
?
WHERE
?=?
""", ('date', 'myTable', 'date', '2017-05-08')
cursor.execute(query)
And I get an error:
TypeError: The first argument to execute must be a string or unicode query.
For the right answer I'd want to:
Keep the question mark format to avoid SQL injection attacks
Keep the triple quotes format so I can write long SQL queries and not loose code readability.
Is there a way to achieve this? I know I could use """ %s """ %('table') format type but that defeats the purpose of this question.
You have 2 issues:
query is a tuple. The way to execute a parameterized query is as
follows:
query = """SELECT ?,count(*)
FROM ?
WHERE ?=? """
args = ('date', 'myTable', 'date', '2017-05-08')
cursor.execute(query, args)
You could pass query with *. This would expand query to a string and a tuple which is what execute expects:
cursor.execute(*query) # 'query' here is defined as it is in your example
But, that won't work. You can not use parameterized query to use parameters in the select and from clauses. You can also not use parameters for the column name in the where clause.
You (usually) don't have to worry about SQL injection if the value isn't inputted by the user (or if the user can't change it in anyway).

SQLAlchemy add condition to query

Beginning of my raw sql statement looks like this:
select if(substr(Table.order_id,1,8)='STRING', Table1.name, t=Table2.type)
Tried to rewrite it in SQLAlchemy query:
query = db_session.query(Table,\
Table1.name if Table.order_id[1:8] == 'STRING' else Table2.type)
But it returned Operator 'getitem' is not supported on this expression.
How can I add this condition to my ORM query without touching models?
Or how can I add raw sql statement in query parameters?
PS: I would prefer not to make any changes to models.
You need to use Functions:
from sqlalchemy import func
q = db_session.query(
func.IF(func.substr(Table.order_id, 1, 8) == 'STRING', Table1.name, Table2.type)
)

How do I do a "starts with" query using SQL alchemy?

I am learning to use SQL alchemy to connect to a mysql database. I want to pull records from the DB that start with a given string. I know that for simple equality all I need to do is this
queryRes = ses.query(Table).filter(Table.fullFilePath == filePath).all()
result = []
How do I do something like this?
queryRes = ses.query(Table).filter(Table.fullFilePath.startsWith(filePath)).all()
result = []
Maybe the query would look like this?
q = ses.query(Table).filter(Table.fullFilePath.like('path%')).all()
SQLAlchemy has a startswith column property, so it works exactly as you'd think:
queryRes = ses.query(Table).filter(Table.fullFilePath.startswith(filePath)).all()
This is the pure SQL:
SELECT * FROM table WHERE field LIKE "string%"
The SQL alchemy is:
q = ses.query(Table).filter(Table.fullFilePath.like('path%')).all()
If you need a case insensitive comparison, use ilike:
session.query(SomeTable).filter(SomeTable.some_column.ilike('bla%')).all()

Parameterized queries with psycopg2 / Python DB-API and PostgreSQL

What's the best way to make psycopg2 pass parameterized queries to PostgreSQL? I don't want to write my own escpaing mechanisms or adapters and the psycopg2 source code and examples are difficult to read in a web browser.
If I need to switch to something like PyGreSQL or another python pg adapter, that's fine with me. I just want simple parameterization.
psycopg2 follows the rules for DB-API 2.0 (set down in PEP-249). That means you can call execute method from your cursor object and use the pyformat binding style, and it will do the escaping for you. For example, the following should be safe (and work):
cursor.execute("SELECT * FROM student WHERE last_name = %(lname)s",
{"lname": "Robert'); DROP TABLE students;--"})
From the psycopg documentation
(http://initd.org/psycopg/docs/usage.html)
Warning Never, never, NEVER use Python string concatenation (+) or string parameters interpolation (%) to pass variables to a SQL query string. Not even at gunpoint.
The correct way to pass variables in a SQL command is using the second argument of the execute() method:
SQL = "INSERT INTO authors (name) VALUES (%s);" # Note: no quotes
data = ("O'Reilly", )
cur.execute(SQL, data) # Note: no % operator
Here are a few examples you might find helpful
cursor.execute('SELECT * from table where id = %(some_id)d', {'some_id': 1234})
Or you can dynamically build your query based on a dict of field name, value:
query = 'INSERT INTO some_table (%s) VALUES (%s)'
cursor.execute(query, (my_dict.keys(), my_dict.values()))
Note: the fields must be defined in your code, not user input, otherwise you will be susceptible to SQL injection.
I love the official docs about this:
https://www.psycopg.org/psycopg3/docs/basic/params.html

Categories