Querying MySQL database using Python - python

I have a table named 'staff', with several columns, but the two I'm interested in are 'username' and 'password'. I am trying to create a prompt which asks the user for their username (and later I will do password) and checks the table in the database to see if that username exists. I am a bit clueless about how to do this, but this is what I have coded so far.
import MySQLdb
db = MySQLdb.connect(host="127.0.0.1", user="root", passwd="", db="sakila")
cur = db.cursor()
search = raw_input("Enter username: ")
query = ('SELECT username, password FROM staff')
cur.execute(query)
results = cur.fetchall()
if search in results:
print("username found")
else:
print("invalid username")
db.close()

EDIT: Make sure to check the comment from #FallenAngel at the bottom and check for security risks in your code!
As Gordon mentioned, you should use a WHERE clause to make things easier. Like so:
import re
import MySQLdb
db = MySQLdb.connect(host="127.0.0.1", user="root", passwd="", db="sakila")
cur = db.cursor()
search = raw_input("Enter username: ")
user_regex = # input a regex here to make sure the username matches a format which avoids SQL injection.
if re.search(user_regex, search):
query = ("SELECT password FROM staff WHERE username = %s", (search,)) # add a WHERE clause
cur.execute(query)
results = cur.fetchall()
else:
results = None
if results: # Now we just need to check if the queryset is empty or not.
print("username found")
else:
print("invalid username")
db.close()
General rule of thumb is to try and make SQL do the searching for things, it's built for it so will be faster than Python.
Make sure your username column is a primary key (or a unique one at least) so you don't have duplicates.
EDIT: Based on #FallenAngels point, you shouldn't directly inject user input into an SQL query as it would expose you to SQL injection.
EDIT2:
First note that we are no longer using "%s" % var formatting for this solution as it is unsafe! Instead we are using "%s", (var,) formatting which is for db queries.
%s (it can also be %d, %n and a few other letters) in a string is a form of string formatting used to insert variable content into a string.
Essentially if you say:
"some stuff here: %s" % var
Anything that is in var will replace the %s in the string. There are several intricacies so it's worth reading more here: https://docs.python.org/2/library/string.html

Perhaps you need to distribute this in two files
In first file you please build the form and through app.route you link it to the def in python file.
This way you can have your presentation and business model completely separate and that will remain more maintainable as well.
Please let me know if you need more simplification along with the code.

Related

Can python cursor.execute accept multiple queries in one go?

Can the cursor.execute call below execute multiple SQL queries in one go?
cursor.execute("use testdb;CREATE USER MyLogin")
I don't have python setup yet but want to know if above form is supported by cursor.execute?
import pyodbc
# Some other example server values are
# server = 'localhost\sqlexpress' # for a named instance
# server = 'myserver,port' # to specify an alternate port
server = 'tcp:myserver.database.windows.net'
database = 'mydb'
username = 'myusername'
password = 'mypassword'
cnxn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+';PWD='+ password)
cursor = cnxn.cursor()
#Sample select query
cursor.execute("SELECT ##version;")
row = cursor.fetchone()
while row:
print(row[0])
row = cursor.fetchone()
Multiple SQL statements in a single string is often referred to as an "anonymous code block".
There is nothing in pyodbc (or pypyodbc) to prevent you from passing a string containing an anonymous code block to the Cursor.execute() method. They simply pass the string to the ODBC Driver Manager (DM) which in turn passes it to the ODBC Driver.
However, not all ODBC drivers accept anonymous code blocks by default. Some databases default to allowing only a single SQL statement per .execute() to protect us from SQL injection issues.
For example, MySQL/Connector ODBC defaults MULTI_STATEMENTS to 0 (off) so if you want to run an anonymous code block you will have to include MULTI_STATEMENTS=1 in your connection string.
Note also that changing the current database by including a USE … statement in an anonymous code block can sometimes cause problems because the database context changes in the middle of a transaction. It is often better to execute a USE … statement by itself and then continue executing other SQL statements.
Yes, it is possible.
operation = 'SELECT 1; INSERT INTO t1 VALUES (); SELECT 2'
for result in cursor.execute(operation, multi=True):
But it is not a comprehensive solution. For example, in queries with two selections, you have problems.
Consider that two types of answers must be fetch all in the cursor!
So the best solution is to break the query to sub queries and do your work step by step.
for example :
s = "USE some_db; SELECT * FROM some_table;"
s = filter(None, s.split(';'))
for i in s:
cur.execute(i.strip() + ';')
in the pyodbc documentation should give you the example your looking for. more over in the GitHub wiki: https://github.com/mkleehammer/pyodbc/wiki/Objects#cursors
you can see an example here:
cnxn = pyodbc.connect(...)
cursor = cnxn.cursor()
cursor.execute("""
select user_id, last_logon
from users
where last_logon > ?
and user_type <> 'admin'
""", twoweeks)
rows = cursor.fetchall()
for row in rows:
print('user %s logged on at %s' % (row.user_id, row.last_logon))
from this example and exploring the code, I would say your next step is testing a multi cursor.execute("<your_sql_Querie>").
if this test works, maybe try and create a CLASS then create instances of that class for each query you want to run.
This would be the basic evolution of a developers effort of reproducing documentation...hope this helps you :)
Yes, you can results for multiple queries by using the nextset() method...
query = "select * from Table1; select * from Table2"
cursor = connection.cursor()
cursor.execute(query)
table1 = cursor.fetchall()
cursor.nextset()
table2 = cursor.fetchall()
The code explains it... cursors return result "sets", which you can move between using the nextset() method.

Passing arguments to mysql using python

I am trying to write a program in Python that accepts the user inputs and queries from the MySQL database.
My database has the following tables:-
Departments(dept_no (primary key), dept_name)
Employees(emp_no(primary key), f_name, l_name, dob, hire_date)
Salaries(emp_no(primary key), salary, from_date(primary key), to_date)
When I give the following input:- Display the employees with salary greater than 20000.
Then the program should perform the following action:-
from sqlite3 import Error
from MySQLdb import connect
def mysql_code():
try:
with connect(host="localhost", user="root", password="root", database="employees") as connection:
with connection.cursor() as cursor:
cursor.execute("SELECT e.emp_no, e.first_name, e.last_name, s.salary from employees e inner join "
"salaries s on e.emp_no = s.emp_no where s.salary > '20000'")
records = cursor.fetchall()
print(records)
print("Total results found = ", cursor.rowcount)
except Error as e:
print(e)
and display the results.
Is it possible to do so or do I have to write code for each possible query?
I previously used:
cursor.execute("SELECT * FROM {} WHERE {} like %s".format(table, column), (text,))
When I defined the query and gave the user options to choose from where I wrote a query for each of the possible user inputs like
to display all records,
search records with the first name and so on. When the user chose an option the result was displayed.
Now I want the user to give inputs such as
Display employees with salaries greater than 20000 working in dept_no d002. or similar queries.
The program should accept queries in the form of a string from the user.
The code should join the tables and display the result by joining the emp_no, first_name, last_name, salary, dept_no from the tables employees, salaries and departments respectively.
you have an error in your code the like after the on comparison is wrong
from sqlite3 import Error
from MySQLdb import connect
def mysql_code():
try:
with connect(host="localhost", user="root", password="root", database="employees") as connection:
with connection.cursor() as cursor:
cursor.execute("SELECT e.emp_no, e.first_name, e.last_name, s.salary from employees e inner join "
"salaries s on e.emp_no = s.emp_no where s.salary > '20000'")
records = cursor.fetchall()
print(records)
print("Total results found = ", cursor.rowcount)
except Error as e:
print(e)
If I'm understanding your question correctly, you want to perform customizable MySQL queries in Python, creating a constant function and passing it the query.
For this I would use f strings. With this you would create a string query
cursor.execute(f"SELECT {values_to_select} \
from {table_name} \
where {filters} \
{more_stuff/extra_queries}")
This way you can pass the values to the function and it will perform the query with the values specified.
Note: As a test this is fine, but if this code is going to be used in production, giving the end user access to queries can be a huge security risk, checkout SQL injection to see how these kinds of attacks happen. I would recommend to sanitize the given strings first so they can't create custom SQL queries.

SQL Select where not working

I am using Python/Flask and trying to query my DB.
conn = sqlite3.connect('./flaskdb.db')
cur = conn.cursor()
cur.execute('SELECT email FROM users WHERE email=\'%s\'', "name")
I have 2 columns, email, password and the value name, password as one of the row/entries.
Why isn't this working? I get the error:
sqlite3.ProgrammingError: Incorrect number of bindings supplied. The current statement uses 0, and there are 7 supplied.
I think you are getting bogged down with using prepared statements here. Try this code:
conn = sqlite3.connect('./flaskdb.db')
cur = conn.cursor()
name = 'someone#somewhere.com'
cur.execute('SELECT email FROM users WHERE email=?', (name,))
Corrections include using ? as a placeholder instead of %s, the latter which is what might be used for other databases. Also, if you want to bind a variable called name, then it too should not have quotes around it.
I have a solution:
cur.execute('SELECT password FROM users WHERE email=(?)', (email,))
you need it as a tuple and (?) as a placeholder.

SELECT FROM WHERE: 2 variables

I have a database with username and password. I want to search for both these to be matching and if it matches, user can login (and if not error shows).
However the if condition of my code doesn't ever fulfil (even when username and password match).
My SQL query must be wrong, I tried finding a solution to searching for more than one variable in a query but couldn't find an answer that worked for me unfortunately, hence why I've asked.
def userLogin(self):
username, password = self.getUserPass()
con = sql.connect("database.db")
cur = con.cursor()
cur.execute("SELECT * FROM users WHERE (?) AND (?)", (username, password))
if cur.fetchmany(size=1):
print("Username and Password match!")
else:
print("Wrong username and password combination.")
Parameter substitution simply inserts the contents of the given variables into the query. So for user "myuser" and password "mypass", the query you're running is:
SELECT * FROM users WHERE ('myuser') AND ('mypass')
This isn't going to do what you expect because you haven't specified which columns to check. Start by writing a full example query that you could run from an interactive SQLite shell. For example, if your username column is called "user" and your password column is called "pass", your query might be:
SELECT * FROM users WHERE user='myuser' AND pass='mypass'
Then just replace the variable values (including quotes if it's a string) with question marks:
SELECT * FROM users WHERE user=? AND pass=?
Substitute that query into your code and it should act as you expect.

Will this code prevent SQL injection (Python)

Please let me know will following codes 100% prevent SQL injection in python
Sample1
username = request.GET('username') # non-filtered user input
connection.execute("SELECT id,name,email FROM user WHERE username=%s LIMIT 1", (username,))
Sample2
username = request.POST('username') # non-filtered user input
name = request.POST('name') # non-filtered user input
email = request.POST('email') # non-filtered user input
connection.execute("UPDATE user SET name=%s, email= %s WHERE username=%s LIMIT 1", (name, email, username,))
The concept of the %s is to isolate the data from the query. When you pass two arguments, they are combined in the module. It is intended to mitigate injection, but I'd be hesitant to say "100%"
Edit: many wiser than myself (maybe even real life security experts!) have weighed in here:
https://security.stackexchange.com/questions/15214/are-prepared-statements-100-safe-against-sql-injection

Categories