Too few parameters error, while no parameters placeholders used - python

I am trying to execute SQL query within Access database using PYODBC and I get following error:
pyodbc.Error: ('07002', '[07002] [Microsoft][ODBC Microsoft Access Driver]
Too few parameters. Expected 1. (-3010) (SQLExecDirectW)')
The problem is that I am not using any additional parameters. Here is the code:
access_con_string = r"Driver={};Dbq={};".format(driver, base)
cnn = pyodbc.connect(access_con_string)
db_cursor = cnn.cursor()
expression = """SELECT F_ARODES.ARODES_INT_NUM, F_ARODES.TEMP_ADRESS_FOREST,F_AROD_LAND_USE.ARODES_INT_NUM, F_ARODES.ARODES_TYP_CD
FROM F_ARODES LEFT JOIN F_AROD_LAND_USE ON F_ARODES.ARODES_INT_NUM = F_AROD_LAND_USE.ARODES_INT_NUM
WHERE (((F_AROD_LAND_USE.ARODES_INT_NUM) Is Null) AND ((F_ARODES.ARODES_TYP_CD)="wydziel") AND ((F_ARODES.TEMP_ACT_ADRESS)=True));"""
db_cursor.execute(expression)
Query itself, if used inside MS-Access works fine. Also, connection is OK, as other queries are executed properly.
What am I doing wrong?

Constants in such queries are problematic - you never know the exact underlying syntax for booleans, strings etc. - even if it works in MS-Access, it can be different inside the intermediary library you're using.
The safest way is to extract them as parameters anyway:
expression = """SELECT F_ARODES.ARODES_INT_NUM, F_ARODES.TEMP_ADRESS_FOREST,F_AROD_LAND_USE.ARODES_INT_NUM, F_ARODES.ARODES_TYP_CD FROM F_ARODES LEFT JOIN F_AROD_LAND_USE ON F_ARODES.ARODES_INT_NUM = F_AROD_LAND_USE.ARODES_INT_NUM WHERE (((F_AROD_LAND_USE.ARODES_INT_NUM) Is Null)
AND ((F_ARODES.ARODES_TYP_CD)=?) AND ((F_ARODES.TEMP_ACT_ADRESS)=?));"""
db_cursor.execute(expression, "wydziel", True)

I had a similar problem, with an update I was trying to perform with pyodbc. When executed in Access, the query worked fine, same for when using the application (it allows some queries from within the app). But when ran in python with pyodbc the same text would throw errors. I determined the problem is the double quote (OP's query has a set of them as well). The query began to work when I replaced them with single quotes.
This does not work:
Update ApplicationStandards Set ShortCutKey = "I" Where ShortName = "ISO"
This does:
Update ApplicationStandards Set ShortCutKey = 'I' Where ShortName = 'ISO'

Related

How to set result of databricks python connector for when explaining SQL queries

My aim is to validate a given SQL string without actually running it.
I thought I could use the EXPLAIN statement to do so.
So I tried using the databricks-sql-connector for python to explain a query, and so determine whether it's valid or not. Example python code:
import databricks.sql
with databricks.sql.connect(...) as connection:
with connection.cursor() as cursor:
cursor.execute("EXPLAIN SELECT BAD-QUERY AS FOO")
r = cursor.fetchall()
The problem with that implementation is that the driver does not throws an error, but instead retrieves me a string containing the error details.
Why it's a problem? I need to parse the string result to distinguish if the explained query was valid or not.
So I was wondering if there some kind of setting / parameter / configuration or so I can use to change the described above result.
Many thanks in Advance!

ORA-01036: illegal variable name/number, binding [duplicate]

I'm using cx_Oracle in Python and can't get a variable be used as table name, like in this simple example:
query = "select * from some.:usertable.userinfo"
bindvars = {'usertable':usertable}
cursor.execute(query, bindvars)
What is the correct syntax? Variable substition works fine when I use WHERE… etc. but not with table names. I guess I have to separate ":usertable" somehow…
Database adapters rarely support using parameters for anything that isn't a 'value' (something that needs quoting). Either use string formatting (dodgy, you run the risk of a sql injection) or use a library like SQLAlchemy that let's you produce valid SQL using Python code.
If you are certain your usertable value is sane (checked against a list of existing table names, for example), the following would work:
query = 'select * from some.{usertable}.userinfo'.format(usertable=usertable)
You cannot bind an object name in Oracle, only a literal. Oracle does, however, have an inbuilt package dbms_assert, to help prevent SQL injection when using dynamic object names. The most useful function in your case is probably sql_object_name, which:
"... verifies that the input parameter string is a qualified SQL
identifier of an existing SQL object."
For instance you could do the following in cx_Oracle.
object_name = cursor.callfunc('sys.dbms_assert.sql_object_name'
, cx_Oracle.string, ['usertable'])
It raises ORA-44002, if the name is invalid, which you can capture in cx_Oracle, or if everything's fine continue as Martijn has suggested.
I would recommend reading Oracle's guide to guarding against SQL injection.
Perhaps it's a bit late to reply, but I was dealing with the same thing 2 days ago.
The solution is, as Martjin says, to format the query.
query = f'select * from {tableName}'
Hope it helps someone as it helped me.

inconsistent results from LIKE query: pyodbc vs. Access

I got a bunch of queries that should be executed in an Access database as a part of my Python script. Unfortunately, queries that used directly in MS Access are giving some records of output, in Python script return nothing (no error either). Connection with database and general syntax should be fine as simple queries (like select one column from table where something) are working just fine. Here is a code with one of these given queries:
import pyodbc
baza = r"C:\base.mdb"
driver = "{Microsoft Access Driver (*.mdb, *.accdb)}"
access_con_string = r"Driver={};Dbq={};".format(driver, baza)
cnn = pyodbc.connect(access_con_string)
db_cursor = cnn.cursor()
expression = """SELECT F_PARCEL.PARCEL_NR, F_PARCEL_LAND_USE.AREA_USE_CD, F_PARCEL_LAND_USE.SOIL_QUALITY_CD, F_ARODES.TEMP_ADRESS_FOREST, F_SUBAREA.AREA_TYPE_CD, F_AROD_LAND_USE.AROD_LAND_USE_AREA, F_PARCEL.COUNTY_CD, F_PARCEL.DISTRICT_CD, F_PARCEL.MUNICIPALITY_CD, F_PARCEL.COMMUNITY_CD, F_SUBAREA.SUB_AREA
FROM F_PARCEL INNER JOIN (F_PARCEL_LAND_USE INNER JOIN ((F_ARODES INNER JOIN F_AROD_LAND_USE ON F_ARODES.ARODES_INT_NUM = F_AROD_LAND_USE.ARODES_INT_NUM) INNER JOIN F_SUBAREA ON F_ARODES.ARODES_INT_NUM = F_SUBAREA.ARODES_INT_NUM) ON (F_PARCEL_LAND_USE.SHAPE_NR = F_AROD_LAND_USE.SHAPE_NR) AND (F_PARCEL_LAND_USE.PARCEL_INT_NUM = F_AROD_LAND_USE.PARCEL_INT_NUM)) ON F_PARCEL.PARCEL_INT_NUM = F_PARCEL_LAND_USE.PARCEL_INT_NUM
WHERE (((F_ARODES.TEMP_ADRESS_FOREST) Like ?) AND ((F_AROD_LAND_USE.AROD_LAND_USE_AREA)<?) AND ((F_ARODES.TEMP_ACT_ADRESS)= ?))
ORDER BY F_PARCEL.PARCEL_NR, F_PARCEL_LAND_USE.SHAPE_NR;"""
rows = db_cursor.execute(expression, ("14-17-2-03*", 0.0049, True)).fetchall()
for row in rows:
print row
cnn.close()
I know that those queries were generated from query builder in MS Access, so I was wondering that maybe this results in differences, but on the other hand this is still access database.
Anyway it seems, that the problem is in SQL, so I would like to know what elements could possibly result in different output between queries executed directly in MS Access and by pyodbc connection?
You are getting tripped up by the difference in LIKE wildcard characters between queries run in Access itself and queries run from an external application.
When running a query from within Access itself you need to use the asterisk as the wildcard character: "14-17-2-03*".
When running a query from an external application (like your Python app) you need to use the percent sign as the wildcard character: "14-17-2-03%".

Django: Using named parameters on a raw SQL query

I'm trying to execute a raw query that is built dynamically.
To assure that the parameters are inserted in the valid position I'm using named parameters.
This seems to work for Sqlite without any problems. (all my tests succeed)
But when I'm running the same code against MariaDB it fails...
A simple example query:
SELECT u.*
FROM users_gigyauser AS u
WHERE u.email like :u_email
GROUP BY u.id
ORDER BY u.last_login DESC
LIMIT 60 OFFSET 0
Parameters are:
{'u_email': '%test%'}
The error I get is a default syntax error as the parameter is not replaced.
I tried using '%' as an indicator, but this resulted in SQL trying to parse
%u[_email]
and that returned a type error.
I'm executing the query like this:
raw_queryset = GigyaUser.objects.raw(
self.sql_fetch, self._query_object['params']
)
Or when counting:
cursor.execute(self.sql_count, self._query_object['params'])
Both give the same error on MariaDB but work on Sqlite (using the ':' indicator)
Now, what am I missing?
edit:
The format needs to have s suffix as following:
%(u_email)s
If you are using SQLite3, for some reason syntax %(name)s will not work.
You have to use :name syntax instead if you want to pass your params as {"name":"value"} dictionary.
It's contrary to the documentation, that states the first syntax should work with all DB engines.
Heres the source of the issue:
https://code.djangoproject.com/ticket/10070#comment:18

Binding variable to table name with cx_Oracle

I'm using cx_Oracle in Python and can't get a variable be used as table name, like in this simple example:
query = "select * from some.:usertable.userinfo"
bindvars = {'usertable':usertable}
cursor.execute(query, bindvars)
What is the correct syntax? Variable substition works fine when I use WHERE… etc. but not with table names. I guess I have to separate ":usertable" somehow…
Database adapters rarely support using parameters for anything that isn't a 'value' (something that needs quoting). Either use string formatting (dodgy, you run the risk of a sql injection) or use a library like SQLAlchemy that let's you produce valid SQL using Python code.
If you are certain your usertable value is sane (checked against a list of existing table names, for example), the following would work:
query = 'select * from some.{usertable}.userinfo'.format(usertable=usertable)
You cannot bind an object name in Oracle, only a literal. Oracle does, however, have an inbuilt package dbms_assert, to help prevent SQL injection when using dynamic object names. The most useful function in your case is probably sql_object_name, which:
"... verifies that the input parameter string is a qualified SQL
identifier of an existing SQL object."
For instance you could do the following in cx_Oracle.
object_name = cursor.callfunc('sys.dbms_assert.sql_object_name'
, cx_Oracle.string, ['usertable'])
It raises ORA-44002, if the name is invalid, which you can capture in cx_Oracle, or if everything's fine continue as Martijn has suggested.
I would recommend reading Oracle's guide to guarding against SQL injection.
Perhaps it's a bit late to reply, but I was dealing with the same thing 2 days ago.
The solution is, as Martjin says, to format the query.
query = f'select * from {tableName}'
Hope it helps someone as it helped me.

Categories

Resources