Querying Sqlite db with variable - python

Trying to learn Sqlite and I'm not sure I understand why I can't get this code to work:
def get_bday(self):
name = self.input_name()
self.c.execute('SELECT * FROM birthdays WHERE name =?', name)
for row in self.c.fetchall():
print(row)
The name variable is being returned from another method. For this example, I am using "joe smoe" without the quotes as the name variable to perform the query with. When I run the above code I get:
self.c.execute('SELECT * FROM birthdays WHERE name =?', name)
sqlite3.ProgrammingError: Incorrect number of bindings supplied. The current statement uses 1, and there are 8 supplied.
The word "joe smoe" is 8 bindings long if you count the space. But I have no idea what that means. I assumed I could simply pass a variable right to Sqlite just as easily as I pass variables around in Python but that doesn't appear to be the case. I think it has something to do with my very poor understanding of tuples.

SQLite is currently thinking you want to query each individual letter of 'joe smoe'.
All you have to do to avoid this is put name in a container of some kind: a tuple or a list for example:
def get_bday(self):
name = self.input_name()
self.c.execute('SELECT * FROM birthdays WHERE name =?', (name,))
# ^ ^^
for row in self.c.fetchall():
print(row)

Related

Error binding parameter 0 - probably unsupported type python

def get_info(search_by=input("Search by: "), value=input("Value: ")):
search = [
(f"{str(search_by)}", f"{str(value)}")
]
items = c.fetchall()
c.execute("SELECT * FROM credentials WHERE (?) LIKE '(?)'", (search,))
print(f"{item[0]}", f"{item[1]}", f"{item[2]}", f"{item[3]}")
conn.commit()
conn.close()
get_info()
I can't understand why I get this error. I'm trying to make the user choose what they want to search by and the value. (i.e search by "service", value "delivery".
There are several problems here.
You can't use a placeholder for a column name. It can only be used for expressions, and the substitution will be the literal value that comes from the parameter list. If you have a variable column name you need to use ordinary string formatting; you should have a white-list to prevent SQL injection.
Placeholders aren't replaced inside quotes.
You have your values nested too deeply. The second argument to c.execute() should be an iterable, and each element fills in one placeholder. search is a list of a tuple, and then you put it inside another tuple when you write (search,). So the values are nested 2 levels deep instead of being in the tuple argument.
Default values of a function should not be calls to input(). The default values are evaluated when the function is defined, not when it's called. So this will ask you for the inputs when you load the script, and use those same values as defaults every time the function is called.
You call c.fetchall() before you execute the query.
You're missing the for item in items: loop.
conn.commit() isn't needed after SELECT queries, only queries that modify the database. And you shouldn't call conn.close() in this function since it didn't open the connection; other functions may still need to use the connection.
def get_info(search_by=None, value=None):
valid_search_by = ['col1', 'col2', 'col3']
if search_by is None:
search_by = input("Search by: ")
if search_by not in valid_search_by:
raise ValueErr("Invalid search_by")
if value is None:
value = input("Value: ")
c.execute(f"SELECT * FROM credentials WHERE {search_by} LIKE ?", (value,))
items = c.fetchall()
for item in items:
print(f"{item[0]}", f"{item[1]}", f"{item[2]}", f"{item[3]}")
Also, there's no need to use str() in an f-string, since the formatting automatically converts the values to strings.

How to write one function to delete from multiple sql tables using python

I am implementing a student database project which has multiple tables such as student,class,section etc
I wrote a delete_table function which takes parameters table name and value to delete a row from a specific table but there seems to be some sort of syntax error in my code :
def delete_tables(tab_name,attr,value):
c.execute("delete from table=:tab_name where attribute=:attr is value=:value ",{'tab_name':tab_name, 'attr': attr, 'value': value})
input :
delete_tables('section','sec_name','S1')
error text :
c.execute("delete from table=:tab_name where attribute=:attr is value=:value ",{'tab_name':tab_name, 'attr': attr, 'value': value})
sqlite3.OperationalError: near "table": syntax error
I've tried all mentioned answers and what y'all are suggesting is that it'll also be insecure even if it works out. So Do i Have to write functions to delete every table individually instead of going for one single function, and is there any other alternative to this problem where I need not keep on writing n functions for n number of tables?????
Thanks in Advance :))
The problem is that you can't use parametrized queries (that :tab_name) on things others than values (? not sure I am using the right term): table names, column names and SQL keywords are forbidden.
where age > :max_age is OK.
where :some_col > :max_age is not.
where age :comparison_operator :max_age is not OK.
Now, you can build your own query using string concatenation or f strings, but... 🧨 this is a massive, massive SQL injection risk. See Bobby Tables Not to mention that concatenating values into SQL query strings quickly runs into issues when you have to deal with characters, numbers or None. (None => NULL, characters need quotes, numbers dont).
You could possibly build a query using string substitutions that accept only known values for the table and column names and then drives the delete criteria value using a parametrized query on :value.
(While this seems restrictive, letting a random caller determine which tables to delete is just not safe in the least).
Something like:
delete_tables(tab_name,attr,value):
safe_tab_name = my_dict_of_known_table_names[tab_name]
safe_attr = my_dict_of_known_column_names[attr]
# you have to `=`, not `is` here👇
qry = f"delete from {safe_tab_name} where {safe_attr} = :value "
# not entirely sure about SQLite's bind/parametrized syntax.
# look it up if needed.
c.execute(qry, dict(value = value))
Assuming a user only enters value directly, that at least is protected from SQL injection.
You need to have a look at what will be the exact SQL command that will be executed in the python method.
For the method call delete_tables('section', 'sec_name', 'S1') the SQL command that will be generated will be
delete from table=section where attribute=sec_name is value=S1
This will be an invalid command in SQL. The correct command should be
delete from section where sec_name='S1'
So you need to change your python function accordingly. The changes that need to be done should be as follows:
def delete_tables(tab_name, attr, value):
c.execute("delete from :tab_name where :attr = ':value'",
{'tab_name': tab_name, 'attr': attr, 'value':value})
def delete_tables(tab_name, attr, value):
c.execute("delete from " + tab_name + "where " + attr + " = " + value)
I think something like that will work, the issue is that you are trying to modify an attribute but its name is always attribute, for that you would like to make it a parameter in order to properly handle it.
Hope it helped.
Edit:
Check this SQLite python
What the c.execute does is to 'execute' a SQL query, so, you can make something like c.execute("select * from clients") if you have a clients table.
execute makes a query and brings you the result set (if it is the case), so if you want to delete from your table using a normal SQL query you would type in the console delete from clients where client_id = 12 and that statement will delete the client with id equal to 12.
Now, if you are using SQLite in python, you will do
c.execute("delete from clients where client_id = 12")
but as you wish it to be for any table and any field (attribute) it turns in the table name, the field name and the value of that field being variables.
tableName = "clients"
field = "client_id"
value = "12" #must be string because you would have to cast it from int in the execute
"""
if value is a varchar you must write
value = "'12'" because the '' are needed.
"""
c.execute("delete from " + tableName + " where " + field + " = " + value)
and in the top of that, as you want it to be a function
def delete_tables(tableName, field, value):
c.execute("delete from " + tableName+ "where " + field + " = " + value)
Edit 2:
aaron's comment is true, it is not secure, the next step you would do is
def delete_tables(tab_name, attr, value):
#no ':value' (it limits the value to characters)
c.execute("delete from :tab_name where :attr = :value",
{'tab_name': tab_name, 'attr': attr, 'value':value})
It is from Vatsal's answer

sqlite3.ProgrammingError: Incorrect number of bindings supplied. The current statement uses 7, and there are 1 supplied

I am currently working on a gym membership system in tkinter and sqlite3. I have this ongoing error and I do not know what is wrong. I cannot figure out why there is an incorrect number of bindings supplied.
def new_user(self):
#Establish Connection
with sqlite3.connect('Gym.db') as db:
c = db.cursor()
#Find Existing username if an action is taken
find_user = ('SELECT * FROM member WHERE username = ?')
c.execute(find_user,[(self.n_Username.get())])
if c.fetchall():
ms.showerror('Error!','Username Taken Try a Diffrent One.')
else:
ms.showinfo('Success!','Account Created!')
self.main_page()
sql = "INSERT INTO 'Member' (FirstName,LastName,Email,Phone,Balance,Username,Password) VALUES(?,?,?,?,?,?,?)" #<-------
c.execute(sql,[(self.n_FirstName.get(),self.n_LastName.get(),self.n_Email.get(),self.n_Phone.get(),self.n_Balance.get(),self.n_Username.get(),self.n_Password.get())])
db.commit()
The error:
sqlite3.ProgrammingError: Incorrect number of bindings supplied. The current statement uses 7, and there are 1 supplied
You are providing a list containing one tuple as the second argument to the execute method. This leads to the "1 supplied" problem as the tuple within the list counts as just a single binding.
Instead, remove the list and simply pass the contained tuple. i.e.
c.execute(sql,
[(self.n_FirstName.get(),
self.n_LastName.get(),
self.n_Email.get(),
self.n_Phone.get(),
self.n_Balance.get(),
self.n_Username.get(),
self.n_Password.get()
)]
)
should be:
c.execute(sql,
(self.n_FirstName.get(),
self.n_LastName.get(),
self.n_Email.get(),
self.n_Phone.get(),
self.n_Balance.get(),
self.n_Username.get(),
self.n_Password.get()
)
)
correct your code as following will work:
c.execute(find_user,(self.n_Username.get(),))
Remove "[]" and add a ','. The comma is important as well, because the second parameter should be a tuple.

Cleaning up sqlite query in Python

Trying to self teach Python and Sqlite and my head is spinning. How does one 'clean up' the output of a query to get rid of all the brackets, commas, etc... from the result. Would also like to .title() the 2nd column. For example:
def get_all_bdays(self):
print("\n" * 100)
print("The birthdays we know about are: ")
self.c.execute('SELECT * FROM birthdays')
for row in self.c.fetchall():
print(row)
Results in the following output:
The birthdays we know about are:
(1, 'joe smoe', '12-12-1212')
How does one go about reformatting that mess to something like:
The birthdays we know about are:
1. Joe Smoe 12-12-1212
My end goal is to create an inventory system for my small business that my employees can use to find where backstock items are located in my storeroom. Thinking about using Flask for something like that, but I'm a long way off from that point in time.
Each row is a tuple with three values: the number, name, and birthday. print(row) is outputting the tuple, with all its parentheses and quotes, not any formatted version.
In Python, you can deconstruct the tuple and assign the parts of it to different variables, then format using Python's syntax for printf-like formatting:
for row in self.c.fetchall():
number, name, date = row
print("%d. %s on %s" % (number, name.title(), date))
or even:
for number, name, date in self.c.fetchall:
print("%d. %s on %s" % (number, name.title(), date))
When you print(row) you are getting the Python representation of row, which includes the quotes and commas and such. What you want to do is to str.format the data into whatever shape you like:
fmt = "{0}. {1}, on {2}"
for row in self.c.fetchall():
print(fmt.format(*row))

Update MySQL field's row

I am using Python 2.7 and MySQLdb. I have made a database which has a table called test and in it two fields (id, firstname). The whole script is about counting and showing how many same firstnames do we have. The part of the script is like this right now but of course it doesn't work due to the 2nd line:
Value = int(input("Type your first name: "))
x.execute("UPDATE test SET id=(last id)+1 WHERE firstname=%s", (Value,))
What I am trying to do is type a Firstname from my keyboard and upload that to the database. If there is already that firstname in the table change his id which is a VARCHAR and make the new id = Last id + 1. For example if I type Doe and there is also Doe as a firstname in the database change Doe's row id number which is 1 by adding 1 and making it 2.
Sorry for the late answer.
I highly suggest to make the ID an INT with AUTO INCREMENT and PRIMARY KEY in order to prevent duplicates, but this would not allow you to change it freely as you want.
You need to check if there is a duplicate of the name you wrote inside the database, am i right?
If there is not, add a new row.
If there is, increment that name's id by one.
In order to check if that name is already in the database, you will need to do a SQL query, then check the results and compare the names you found with the one you wrote.
First, the query to get the names already in the database.
Then, check if the name already exists.
Then again, update or insert a new line into the database.
x.execute("""SELECT firstname FROM test""")
for row in x:
result = row[0]
if result == name:
nameExists = True
else:
nameExists = False
if !nameExists:
x.execute("""INSERT INTO test(firstname, id) VALUES (%s), (%s)""", name, id)
else:
x.execute("""SELECT id FROM test WHERE firstname = %s""", name)
for row in x:
actualId = row[0]
actualId = actualId + 1;
x.execute("""UPDATE test SET id = %s WHERE firstname = %s""", actualId, name)
The code above may change based on your variable names or preferences.
Do not take it as working, and copy-paste it directly into your code.
I hope the answer will be satysfing and complete. If not, let me know. It's the first time i answer to a question, and i may have not done it properly.
Thank you for your understanding.

Categories