psycopg2 strange behavior - django

from django.db import connection
q = 'some value'
sql1 = 'SELECT * FROM table WHERE field LIKE %%%s%%' % q
sql2 = 'SELECT * FROM table WHERE field LIKE %%'+ q +'%%'
cursor = connection.cursor()
cursor.execute( sql1 ) #why exception: IndexError: tuple index out of range ?
cursor.execute( sql2 ) #works ok

You need to QUOTE properly your SQL arguments.
And by quoting properly I mean using the quote facility provided by DBAPI, not adding a ' around your string, which is useless.
Correct code :
q = "%"+q+"%"
cursor.execute( 'SELECT * FROM table WHERE field LIKE %s', (q,) )
Really correct code :
q = "%"+q.replace("%","%%")+"%"
cursor.execute( 'SELECT * FROM table WHERE field LIKE %s', (q,) )
Suppose q = "a'bc"
First, rewrite this as "%a'bc%"
Then use it as a normal string argument. psycopg will rewrite it as '%a\'bc%' as it should.
If q may contain "%" and you want to search for it, then use the second one.

Using direct string manipulation will almost certainly lead to improper SQL that is vulnerable to SQL Injection attacks (see psycopg2's comments on the subject).
What I think you're looking to do is try and perform a LIKE '%some value%' in django, right?:
from django.db import connection
q = '%some value%'
cur = connection.cursor()
cur.execute("SELECT * FROM table WHERE field LIKE %(my_like)s", {'my_like': q})
As of psycopg2 2.4.1, the SQL that is executed on the server is:
SELECT * FROM table WHERE field LIKE '%some value%'

You need to QUOTE properly your SQL command:
sql1 = "SELECT * FROM table WHERE field LIKE '%%%s%%'" % q
sql2 = "SELECT * FROM table WHERE field LIKE '%"+ q +"%'"
And by quoting properly I mean using single quotes with LIKE expressions.

Related

DJANGO How to execute this sql in django.db with execute - SET PREPARE EXECUTE [duplicate]

import mysql.connector
connection = mysql.connector.connect(user="REMOVED",
password="REMOVED",
host="REMOVED",
database="REMOVED")
cur = connection.cursor()
# Latitude - remove letter A
cur.execute("UPDATE tau._inm_exportados_test_csv SET latitud = REPLACE (latitud, 'a=','');")
print("Latitude change remove letter A - executed!")
# Longitude - remove letter A
cur.execute("UPDATE tau._inm_exportados_test_csv SET longitud = REPLACE (longitud, 'a=','');")
print("Longitude change remove letter A - executed!")
# Latitude - MODIFY COLUMN
cur.execute("ALTER TABLE tau._inm_exportados_test_csv MODIFY COLUMN latitud DECIMAL(10,6);")
print("Latitude - MODIFY COLUMN - executed!")
# Longitude - MODIFY COLUMN
cur.execute("ALTER TABLE tau._inm_exportados_test_csv MODIFY COLUMN longitud DECIMAL(10,6);")
print("Longitude - MODIFY COLUMN - executed!")
# Post Code data type change
cur.execute("ALTER TABLE tau._inm_exportados_test_csv MODIFY COLUMN codigo_postal varchar(255);)")
print("Post Code data type change to varchar(255) - executed!")
connection.commit()
cur.close()
connection.close()
I'm trying to make this simple list of statements work without success. What makes it more confusing is that the first four statements work whereas the final one doesn't work even when I comment out the rest! The final statement gets the following reponse:
mysql.connector.errors.InterfaceError: Use multi=True when executing multiple statements
The datatype for codigo_postal is int(11) unlike latitud and longitud which are varchar.
I have tried creating new connections, new cursors, new connections AND cursors. I have tried adding multi="True" and combining statements into one operation. I have tried adding multi="True" to each cur.execute() as both the second and third parameter. I have run the statement in Workbench to ensure the statement is valid and it works.
No success with it here though...
You can use commit after you executed DML (Data Manipulation Language) commands. Also using multi=True can be more convenient to complete this job, but you need to run the generator which created by execute. doc.
Ordinary method:
cur = connection.cursor()
def alter(state,msg):
try:
cur.execute(state)
connection.commit()
except Exception as e:
connection.rollback()
raise e
print(msg)
alter("ALTER TABLE address MODIFY COLUMN id int(15);","done")
alter("ALTER TABLE address MODIFY COLUMN email varchar(35);","done")
alter("ALTER TABLE address MODIFY COLUMN person_id int(35);","done")
With multi=True:
cur = connection.cursor()
def alter(state,msg):
result = cur.execute(state,multi=True)
result.send(None)
print(msg,result)
try:
alter("ALTER TABLE address MODIFY COLUMN id int(45)","done")
alter("ALTER TABLE address MODIFY COLUMN email varchar(25)","done")
alter("ALTER TABLE address MODIFY COLUMN person_id int(25);","done")
connection.commit()
except Exception as e:
connection.rollback()
raise e
I had the same problem.
I wanted my code to be clean and I wanted to have all my commands in a list and just run them in a sequence.
I found this link and this link and finally was able to write this code:
import mysql.connector as sql
from mysql.connector import Error
commands = [
'''
USE sakila;
SELECT * FROM actor;
''',
'''
USE sakila;
SELECT * FROM actor WHERE actor_id < 10;
'''
]
connection_config_dict = {
'user': 'username',
'password': 'password',
'host': '127.0.0.1',
}
try:
connection = sql.connect(**connection_config_dict)
if connection.is_connected():
db_Info = connection.get_server_info()
print("Connected to MySQL Server version ", db_Info, '\n')
cursor = connection.cursor()
for command in commands:
for result in cursor.execute(command, multi=True):
if result.with_rows:
print("Rows produced by statement '{}':".format(
result.statement))
print(result.fetchall())
else:
print("Number of rows affected by statement '{}': {}".format(
result.statement, result.rowcount), '\n')
record = cursor.fetchall()
except Error as e:
print("Error while connecting to MySQL", e, '\n')
finally:
if connection.is_connected():
cursor.close()
connection.close()
print("MySQL connection is closed", '\n')

Django How to select differrnt table based on input?

I have searched for the solution to this problem for a long time, but I haven't got the appropriate method.
Basically All I have is tons of tables, and I want to query value from different tables using raw SQL.
In Django, we need a class representing a table to perform the query, for example:
Routes.objects.raw("SELECT * FROM routes")
In this way, I can only query a table, but what if I want to query different tables based on the user's input?
I'm new to Django, back in ASP.NET we can simply do the following query:
string query = "SELECT * FROM " + county + " ;";
var bus = _context.Database.SqlQuery<keelung>(query).ToList();
Is this case, I can do the query directly on the database instead of the model class, and I can select the table based on the user's selection.
Is there any method to achieve this with Django?
You can run raw queries in Django like this -
From django.db import connection
cursor = connection.cursor()
table = my_table;
cursor.execute("Select * from " + table)
data = cursor.fetchall()

Express the raw sql into Django notation object filter

Is there any way to express the following raw sql into django notation with filter ?
for p in wm.user_visibility.objects.raw('SELECT * FROM user S
LEFT JOIN user_visibility SA
ON S.Id = SA.user_id AND SA.dash_id=' + param +
'WHERE SA.user_id IS NULL'):
print(p.full_name)
user_visibility.objects.filter(dash_id = param, user_id__isnull = True)
P.s.
Your SQL is weird. Joining user and user_visibility on user_id, but then keep only entries where user_visibility.user_id is null.

SqlAlchemy core union_all not adding parentheses

I have the following sample code:
queries = []
q1 = select([columns]).where(table.c.id == #).limit(#)
queries.append(q1)
q2 = select([columns]).where(table.c.id == #).limit(#)
queries.append(q2)
final_query = union_all(*queries)
The generated SQL should be this:
(select columns from table where id = # limit #)
UNION ALL
(select columns from table where id = # limit #)
But, I'm getting
select columns from table where id = # limit #
UNION ALL
select columns from table where id = # limit #
I tried using subquery, as follows for my queries:
q1 = subquery(select([columns]).where(table.c.id == #).limit(#))
The generated query then looks like this:
SELECT UNION ALL SELECT UNION ALL
I also tried doing
q1 = select([columns]).where(table.c.id == #).limit(#)).subquery()
But, I get the error:
'Select' object has no attribute 'subquery'
Any help to get the desired output with my subqueries wrapped in parentheses?
Note: this is not a duplicate of this question, because I'm not using Session.
EDIT
Okay, this works, but I don't believe it is very efficient, and it's adding an extra select * from (my sub query), but it works.
q1 = select('*').select_from((select(columns).where(table.c.id == #).limit(#)).alias('q1'))
So, if anyone has any ideas to optimize, or let me know if this is as good as it gets. I would appreciate it.
The author of SQLAlchemy seems to be aware of this and mentions a workaround for it on the SQLAlchemy 1.1 changelog page. The general idea is to do .alias().select() on each select.
stmt1 = select([table1.c.x]).order_by(table1.c.y).limit(1).alias().select()
stmt2 = select([table2.c.x]).order_by(table2.c.y).limit(2).alias().select()
stmt = union(stmt1, stmt2)

Escaping queries in Django

I have the following method:
def select_query(self):
sql = "SELECT * FROM {t} WHERE 1".format(t=self._meta.db_table)
for column_name in self.distinguishing_column_names():
sql = sql + " AND {c} = {v}".format(c=column_name, v=getattr(self, column_name))
return sql
This will give me a query like this:
SELECT * FROM customer WHERE 1 AND name = JOHN SMITH AND customer_number = 11423 AND social_security_number = 1234567890 AND phone = 2323523353
Obviously, that's not going to work. Is there a way to get Django to quote this for me?
Note: I'm not asking for a prepared statement. That's something different.
Do you need to return a query this way? The proper way would be to call cursor with the query and the params as argument:
Does Python support MySQL prepared statements?
The correct way to format a query seems to be:
query = query % db.literal(args)
Where db is a mysql.Connection (or presumably any connection)
Apparently the answer is "no."