mysql query using python 3.6 (string variable is in single quotes) - mysql

I am new in python as well as mysql. I am having trouble in populating proper query statement for mysql.
sql = "SELECT * FROM Persons WHERE %s"
cur = db.cursor()
cur.execute(sql,(where,))
where is a string variable which creates a string for WHERE clause; this is the point of question. When I print this variable it give the following result:
Gender = True And IsLate = False
(without any quotes) but when I add this variable to the query to execute it, it adds single quotes around the string.
I used the command
print(cur.statement)
and it prints:
SELECT * FROM Persons WHERE 'Gender = True And IsLate = False'
After supplying parameter, it puts it within single quotes and query returns 0 rows.
I have worked around by concatenating the query statement and variable together and execute the string as query, that worked,
sql = sql + where
cur.execute(sql)
But I know that is not the professional way, as I have searched and found the professional way is to use parameterized query and use variable to store the condition(s) and supplying it at the execution of query.
Looking for advice, am I thinking the right way or otherwise?

The whole point of using parameter substitution in cursor.execute() is that it protects you from SQL injection. Each parameter is treated as a literal value, not substituted into the query and re-interpreted.
If you really want it to be interprted, you need to use string formatting or concatenation, as you discovered. But then you will have to be very careful in validating the input, because the user can supply extra SQL code that you may not have expected, and cause the query to malfunction.
What you should do is build the where string and parameter list dynamically.
where = []
params = []
if gender_supplied:
where.append('gender = %s')
params.append(gender)
if islate_supplied:
where.append*('islate = %s')
params.append(islate)
sql = 'select * from persons'
if where:
query = sql + ' where ' + ' and '.join(where)
else:
query = sql
cur.execute(query, params)

Related

Google App Script SQL query returns bool 'True' instead of Int value but query works outside App Script? [duplicate]

When I execute a SQL query from Java and store the boolean returned, the query always returns true which shouldn't be the case at all. So I emptied the table and fired the query again, and yet it returns true for the emptied table. I have attached a picture of the table. I want the query to return true or false, so I can store it in Java. Can someone please specify an alternate code for this, please?
This is my code on java for the query.
boolean avail = st.execute("SELECT EXISTS(SELECT * from sales WHERE product='"+n+"' AND ord_date='"+sqlDate+"');");
And this is my code for result set
Statement st = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
This is the table, name of the table is 'sales'
I'm new to MySQL, a more specific approach is appreciated.
Statement.execute will return true regardless of what the query returns. You are still supposed to retrieve the actual result of the query.
Returns
true if the first result is a ResultSet object; false if it is an update count or there are no results
As you execute an EXISTS statement, there will always be a result (true or false). The actual value still has to be retrieved:
You must then use the methods getResultSet or getUpdateCount to retrieve the result, and getMoreResults to move to any subsequent result(s).
For reference: https://docs.oracle.com/javase/8/docs/api/java/sql/Statement.html#execute-java.lang.String-String
Also note that you are directly embedding strings into your query, this will leave you vulnerable to SQL injections. Please read: How can prepared statements protect from SQL injection attacks?. Recommended reading: Introduction to JDBC
The return value of Statement.execute() signals whether the query produces a result set (true) or - possibly - an update count (false). The query you execute is a select which will always produce a result set (even if empty). In other words, it will always return true for a select.
If you want to get the result of your query, then the recommend approach is to use executeQuery. However, you are also concatenating values into your query string, which is a bad idea because it leave you vulnerable to SQL injection. The recommended approach is to switch to prepared statements:
try (PreparedStatement pstmt = con.prepareStatement(
"SELECT EXISTS(SELECT * from sales WHERE product = ? AND ord_date = ?)")) {
pstmt.setString(1, n);
pstmt.setDate(2, sqlDate);
try (ResultSet rs = st.executeQuery() {
boolean avail = rs.next() && rs.getBoolean(1);
// use avail...
}
}

How to do a SQL query using a string wildcard and LIKE?

I am new to python and currently learning to use SQL with python. I have the following code:
word = input("Enter a word: ")
query = cursor.execute("SELECT * FROM Dictionary WHERE Expression LIKE '%s%' " % word)
results = cursor.fetchall()
The second line throws an error since I don't think I can use '%s%' like that? How would I change this so as to be able to make this work? I want to be able to return all related entries to the users input. So if the user inputs "rain", then I want the query to return all possible results e.g. "raining", "rainy" etc. Thank you.
You can try
query = cursor.execute(f"SELECT * FROM Dictionary WHERE Expression LIKE '%{word}%' ")
You should use cursor.execute() parameter substitution rather than string formatting, to prevent SQL injection.
Then use CONCAT() to surround the search string with %.
query = cursor.execute("SELECT * FROM Dictionary WHERE Expression LIKE CONCAT('%', %s, '%' "), (word,))

How to use Oracle's JSON_VALUE function with a PreparedStatement

I am trying to run a SQL query using Oracle's json_value() function using a PreparedStatement.
Assume the following table setup:
drop table foo cascade constraints purge;
create table foo
(
id integer primary key,
payload clob,
constraint ensure_json check (payload IS JSON STRICT)
);
insert into foo values (1, '{"data": {"k1": 1, "k2": "foo"}}');
The following SQL query works fine:
select *
from foo
where json_value(payload, '$.data.k1') = '1'
and returns the expected row.
However, when I try to run this query using a PreparedStatement like in the the following piece of code:
String sql =
"select *\n" +
"from foo\n" +
"where json_value(payload, ?) = ?";
PreparedStatement pstmt = conection.prepareStatement(sql);
pstmt.setString(1, "$.data.k1");
pstmt.setString(2, "1");
ResultSet rs = pstmt.executeQuery();
(I removed all error checking from the example to keep it simple)
This results in:
java.sql.SQLException: ORA-40454: path expression not a literal
The culprit is passing the json path value (parameter index 1), the second parameter is no problem.
When I replace (only) the first parameter with a String constant json_value(payload, '$.data.k1') = ? the prepared statement works fine.
In a desperate attempt, I also tried including the single quotes in the parameter: pstmt.setString(1, "'$.data.k1'") but not surprisingly, Oracle wouldn't accept it either (same error message).
I also tried using json_value(payload, concat('$.', ?) ) and only passing "data.k1" as the parameter - same result.
So, the question is:
How can I pass a JSON path expression to Oracle's json_value function using a PreparedStatement parameter?
Any ideas? Is this a bug in the driver or in Oracle? (I couldn't find anything on My Oracle Support)
Or is this simply a case of "not implemented"?
Environment:
I am using Oracle 18.0
I tried the 18.3 and 19.3 version of the ojdbc10.jar driver together with OpenJDK 11.
It isn't the driver - you get the same thing with dynamic SQL:
declare
result foo%rowtype;
begin
execute immediate 'select *
from foo
where json_value(payload, :1) = :2'
into result using '$.data.k1', '1';
dbms_output.put_line(result.payload);
end;
/
ORA-40454: path expression not a literal
ORA-06512: at line 4
And it isn't really a bug, it's documented (emphasis added):
JSON_basic_path_expression
Use this clause to specify a SQL/JSON path expression. The function uses the path expression to evaluate expr and find a scalar JSON value that matches, or satisfies, the path expression. The path expression must be a text literal. See Oracle Database JSON Developer's Guide for the full semantics of JSON_basic_path_expression.
So you would have to embed the path literal, rather than bind it, unfortunately:
declare
result foo%rowtype;
begin
execute immediate 'select *
from foo
where json_value(payload, ''' || '$.data.k1' || ''') = :1'
into result using '1';
dbms_output.put_line(result.payload);
end;
/
1 rows affected
dbms_output:
{"data": {"k1": 1, "k2": "foo"}}
or for your JDBC example (keeping the path as a separate string as you presumably want that to be a variable really):
String sql =
"select *\n" +
"from foo\n" +
"where json_value(payload, '" + "$.data.k1" + "') = ?";
PreparedStatement pstmt = conection.prepareStatement(sql);
pstmt.setString(1, "1");
ResultSet rs = pstmt.executeQuery();
Which obviously isn't what you want to do*, but there doesn't seem to be an alternative. Other than turning your query into a function and passing the path variable in to that, but then the function would have to use dynamic SQL, so the effect is much the same - maybe easier to handle SQL injection concerns that way though.
* and I'm aware you know how to do this the embedded way, and know you want to use bind variables because that's the correct thing to do; I've spelled it out more than you need for other visitors *8-)

MySQL Dynamic Query Statement in Python with Dictionary

Very similar to this question MySQL Dynamic Query Statement in Python
However what I am looking to do instead of two lists is to use a dictionary
Let's say i have this dictionary
instance_insert = {
# sql column variable value
'instance_id' : 'instnace.id',
'customer_id' : 'customer.id',
'os' : 'instance.platform',
}
And I want to populate a mysql database with an insert statement using sql column as the sql column name and the variable name as the variable that will hold the value that is to be inserted into the mysql table.
Kind of lost because I don't understand exactly what this statement does, but was pulled from the question that I posted where he was using two lists to do what he wanted.
sql = "INSERT INTO instance_info_test VALUES (%s);" % ', '.join('?' for _ in instance_insert)
cur.execute (sql, instance_insert)
Also I would like it to be dynamic in the sense that I can add/remove columns to the dictionary
Before you post, you might want to try searching for something more specific to your question. For instance, when I Googled "python mysqldb insert dictionary", I found a good answer on the first page, at http://mail.python.org/pipermail/tutor/2010-December/080701.html. Relevant part:
Here's what I came up with when I tried to make a generalized version
of the above:
def add_row(cursor, tablename, rowdict):
# XXX tablename not sanitized
# XXX test for allowed keys is case-sensitive
# filter out keys that are not column names
cursor.execute("describe %s" % tablename)
allowed_keys = set(row[0] for row in cursor.fetchall())
keys = allowed_keys.intersection(rowdict)
if len(rowdict) > len(keys):
unknown_keys = set(rowdict) - allowed_keys
print >> sys.stderr, "skipping keys:", ", ".join(unknown_keys)
columns = ", ".join(keys)
values_template = ", ".join(["%s"] * len(keys))
sql = "insert into %s (%s) values (%s)" % (
tablename, columns, values_template)
values = tuple(rowdict[key] for key in keys)
cursor.execute(sql, values)
filename = ...
tablename = ...
db = MySQLdb.connect(...)
cursor = db.cursor()
with open(filename) as instream:
row = json.load(instream)
add_row(cursor, tablename, row)
Peter
If you know your inputs will always be valid (table name is valid, columns are present in the table), and you're not importing from a JSON file as the example is, you can simplify this function. But it'll accomplish what you want to accomplish. While it may initially seem like DictCursor would be helpful, it looks like DictCursor is useful for returning a dictionary of values, but it can't execute from a dict.

Django: raw SQL queries with a dynamic number of variables

Is it possible to construct raw SQL queries in Django so that they accept a dynamic number of arguments? So for example say that I have the following url structure in my app:
/books/category/history/
/books/category/history/1800s/
For the first query, I'm looking for all books with the keyword 'history', and for the second, I'm looking for all books with the keyword 'history' AND the keyword '1800s'.
I currently have two separate queries for each of these:
keyword1 = 'history'
SELECT appname_book.name AS name FROM appname_book WHERE keyword=%s,[keyword1]
keyword1 = 'history'
keyword2 = '1800s'
SELECT appname_book.name AS name FROM appname_book WHERE keyword=%s AND keyword=%s,[keyword1, keyword2]
Anyone know of a cleaner and more efficient way to do this?
I'm using Django 1.3 and MySQL.
Thanks.
Why dont you use Django QuerySet, like this:
Book.objects.all().filter(keyword__in=['history','1800s']).values('name')
Another possible solution using RAW SQL, coud be:
keywords = []
SQL = 'SELECT appname_book.name AS name FROM appname_book WHERE 1=1 '
SQL += ' '.join(['AND keyword=%s' for _ in params])
Sure, you could do something like this to dynamically generate a raw SQL query
sql = 'SELECT id FROM table WHERE 1 = 1'
params = []
if 'description' in args.keys():
sql += ' AND description LIKE %s'
params.append('%'+args['description']+'%')
if 'is_active' in args.keys():
sql += ' AND is_active LIKE %s'
params.append(args['is_active'])
... you can put as many "ifs" you want to construct the query
with connections['default'].cursor() as cursor:
cursor.execute(sql, params)
This way would still be completely safe against SQL Injections vulnerability