i'm using sqlite to organize data fetched with a scraper script, having trouble executing an 'insert into' command.
I'm making a scraper for an electronics website as a newbie Python enthusiast.
I already had a working script that would scrape all the pages up until when i decided to modify the code to create a new column with prices and name that column using today's date.
Now for some reason the SQL command that inserts the data to the table refuses to execute with the new column i add.
Tried adding the new column to the SQL command with ? method and .format() method with no success.
Tried all kinds for ' locations around the ?s and the {}s.
This is the Code:
class Product:
def __init__(self, prodId, title, price=None):
self.prodId = prodId
self.title = title
self.price = price
self.currDatePriceCloumnName = date + 'Price'
def insertToTable(self):
self.addColumn()
conn = sqlite3.connect(databaseName)
c = conn.cursor()
c.execute("insert into {} (?,?,?) values (?,?,?)".format(table),('Product_ID','Title',str(self.currDatePriceCloumnName),str(self.prodId),str(self.title),str(self.price)))
conn.commit()
conn.close()
def addColumn(self):
conn = sqlite3.connect(databaseName)
c = conn.cursor()
try:
c.execute("alter table {} add column '?'".format(table),(str(self.currDatePriceCloumnName),))
conn.commit()
conn.close()
except:
pass
i expected the c.execute in the insertToTable to insert the data to the table, but what i get is this error:
File "/home/sergio/Desktop/test/scraper.py", line 67, in insertToTable
c.execute("insert into {} (?,?,?) values (?,?,?)".format(table),('Product_ID','Title',str(self.currDatePriceCloumnName),str(self.prodId),str(self.title),str(self.price)))
sqlite3.OperationalError: near "?": syntax error
The weird thing is that the the column is created but not filled.
When i used the .format() method, the error had the desired column name instead of the ?, this tells me that the issue might be with the fact im using self.currDatePriceCloumnName but i'm stuck from here.
Please help..
Thanks in advance! =]
You have a typo in:
c.execute("insert into {} (?,?,?) values (?,?,?)".format(table),('Product_ID','Title',str(self.currDatePriceCloumnName),str(self.prodId),str(self.title),str(self.price)))
At:
values (?,?,?)".format(table),
The end of .format(table) is all you have inserted into the string. The extra ) is causing the .format() to end throwing a syntax error because it's not expecting a ,. You're not passing any other values.
That being said don't use string formatting with SQL statements in the future (as a general side note) as it is bad practice for security purposes:
https://www.w3schools.com/sql/sql_injection.asp
Related
I am working with mySQL in Python with the PyMySQL module handling the connection. dbCursor is the cursor object.
I have the following statement:
statement = f"INSERT INTO Machine_status (timestamp, num_of_char, STATUS, emergency_status) VALUES ({timestamp},{num_of_char},{status},{emergency_status})"
dbCursor.execute(statement)
However, whenever I try to execute this statement, the program hangs. This is perplexing because this statement similar to it:
statement = f"INSERT INTO Speed (timestamp, meters_per_second) VALUES ({timestamp}, {meters})"
dbCursor.execute(statement)
does not give any issue. What is wrong with the first statement? I'm truly at a loss as to what it could be. I checked and made sure that the variable names match, I checked the structure in the database to make sure the variables matched what the columns could accept, made sure there were no extra parentheses and that syntax was correct but it continues to hang when trying to execute the statement. The frustrating part is that trying the run the statement directly in the database (using phpMyAdmin) gives no issues. What's going on here?
EDIT1: Added more relevant code.
EDIT2: I've already tried reformatting it to using placeholders instead (i.e. VALUES (%d, %d, %s, %d)). It doesn't resolve the issue.
EDIT3: CREATE TABLE statement
"CREATE TABLE Machine_status (id AUTO_INCREMENT PRIMARY KEY, timestamp int, num_of_char int, status varchar(255), emergency_status int)"
Complete insert string
payload = item[0].split("{")[1].split("}")[0].split(" ") #returns a list containing everything between brackets of statement
timestamp = int(payload[0])
num_of_char = int(payload[1])
status = payload[2]
emergency_status = int(payload[3])
statement = "INSERT INTO Machine_STATUS (timestamp, num_of_char, STATUS, emergency_status) VALUES (%d, %d, %s, %d)"
try:
dbCursor.execute(statement, (timestamp, num_of_char, status, emergency_status))
print("Complete.")
except:
print("something went wrong")
EDIT4: I figured out what the issue was. pymysql was giving me issues with trying to extract the error code, so I switched to mysql.connector. It finally gave me an error of "table not found". Apparently the table name I was using in the code didn't match exactly what I was using in the database, and therefore it was causing the table to not be found. I changed it, and now the issue is gone.
As Klaus already wrote don't use stirng insertion use place holders.
Like
statement = "INSERT INTO Machine_status (timestamp, num_of_char, STATUS, emergency_status) VALUES (%s,%s,%s,%s)"
dbCursor.execute(statement,(timestamp,num_of_char,status,emergency_status))
I'm totally new to Python and SQL but I'm following a tutorial and I really can't get this working. Please ignore the poor string construct but I'll tidy it up when it's working. I'm trying to send two values to insert into a new row on a table, they get passed to it in the cursor execute command.
def update_height(conn, location, timestamp_height):
sql_update_height_table = """ INSERT INTO """ + str(location) + """_cushion(timestamp,height)
VALUES(?,?) """
print (sql_update_height_table)
cur = conn.cursor()
cur.execute(sql_update_height_table,timestamp_height)
conn.commit()
return cur.lastrowid
Result of print(sql_update_height_table):
INSERT INTO 0xEE738a9e_height(timestamp,height)
VALUES(?,?)
and this is the error:
sqlite3.OperationalError: near "0xEE738a9e": syntax error
I'm following this tutorial and I really can't see what I'm doing wrong, I've been looking for hours.
timestamp_height is a tuple with two entries.
I don't know why you need to use a table name like this, but 0x means as usually a HEX representation. Quote the name with backticks.
sql_update_height_table = """ INSERT INTO `""" + str(location) + """_cushion`(timestamp,height)
VALUES(?,?) """
I'm trying to do something extremely simple that works, but not the way I expect it to. I have a database with various tables and for each of those tables, I'm trying to extract the column names from the information schema. I'm using the code below and everything works like a charm (python):
import psycopg2 as pgsql
# code to connect and generate cursor
table = 'some_table_name'
query = 'SELECT column_name FROM information_schema.columns WHERE table_name = %s'
cursor.execute(query, (table,))
result = pd.DataFrame(cursor.fetchall())
print(result)
So far, so good. The problem arises when I replace the query variable with the following:
import psycopg2 as pgsql
# code to connect and generate cursor
table = 'some_table_name'
**query = 'SELECT column_name FROM information_schema.columns WHERE table_name='+table
cursor.execute(query)**
result = pd.DataFrame(cursor.fetchall())
print(result)
If I print the statement, it's correct:
SELECT column_name FROM information_schema.columns WHERE table_name=some_table_name
However, when I run the query, I'm getting this error message:
UndefinedColumn: column "some_table_name" does not exist
LINE 1: ... FROM information_schema.columns WHERE table_name=some_tabl...
some_table_name is a table name as a parameter to the WHERE clause, not a column name. How is this even possible?
Thanks!
Your problem is that you haven't put some_table_name in quotes so it is treated as a column name, not a string literal. Why not stick with the first method which both worked and is in line with the psycopg documentation?
Question: Is it possible to use a variable as your table name without having to use string constructors to do so?
Info:
I'm working on a project right now that catalogs data from a star simulation of mine. To do so I'm loading all the data into a sqlite database. It's working pretty well, but I've decided to add a lot more flexibility, efficiency, and usability to my db. I plan on later adding planetoids to the simulation, and wanted to have a table for each star. This way I wouldn't have to query a table of 20m some planetoids for the 1-4k in each solar system.
I've been told using string constructors is bad because it leaves me vulnerable to a SQL injection attack. While that isn't a big deal here as I'm the only person with access to these dbs, I would like to follow best practices. And also this way if I do a project with a similar situation where it is open to the public, I know what to do.
Currently I'm doing this:
cursor.execute("CREATE TABLE StarFrame"+self.name+" (etc etc)")
This works, but I would like to do something more like:
cursor.execute("CREATE TABLE StarFrame(?) (etc etc)",self.name)
though I understand that this would probably be impossible. though I would settle for something like
cursor.execute("CREATE TABLE (?) (etc etc)",self.name)
If this is not at all possible, I'll accept that answer, but if anyone knows a way to do this, do tell. :)
I'm coding in python.
Unfortunately, tables can't be the target of parameter substitution (I didn't find any definitive source, but I have seen it on a few web forums).
If you are worried about injection (you probably should be), you can write a function that cleans the string before passing it. Since you are looking for just a table name, you should be safe just accepting alphanumerics, stripping out all punctuation, such as )(][;, and whitespace. Basically, just keep A-Z a-z 0-9.
def scrub(table_name):
return ''.join( chr for chr in table_name if chr.isalnum() )
scrub('); drop tables --') # returns 'droptables'
For people searching for a way to make the table as a variable, I got this from another reply to same question here:
It said the following and it works. It's all quoted from mhawke:
You can't use parameter substitution for the table name. You need to add the table name to the query string yourself. Something like this:
query = 'SELECT * FROM {}'.format(table)
c.execute(query)
One thing to be mindful of is the source of the value for the table name. If that comes from an untrusted source, e.g. a user, then you need to validate the table name to avoid potential SQL injection attacks. One way might be to construct a parameterised query that looks up the table name from the DB catalogue:
import sqlite3
def exists_table(db, name):
query = "SELECT 1 FROM sqlite_master WHERE type='table' and name = ?"
return db.execute(query, (name,)).fetchone() is not None
I wouldn't separate the data into more than one table. If you create an index on the star column, you won't have any problem efficiently accessing the data.
Try with string formatting:
sql_cmd = '''CREATE TABLE {}(id, column1, column2, column2)'''.format(
'table_name')
db.execute(sql_cmd)
Replace 'table_name' with your desire.
To avoid hard-coding table names, I've used:
table = "sometable"
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS {} (
importantdate DATE,
somename VARCHAR,
)'''.format(table))
c.execute('''INSERT INTO {} VALUES (?, ?)'''.format(table),
(datetime.strftime(datetime.today(), "%Y-%m-%d"),
myname))
As has been said in the other answers, "tables can't be the target of parameter substitution" but if you find yourself in a bind where you have no option, here is a method of testing if the table name supplied is valid.
Note: I have made the table name a real pig in an attempt to cover all of the bases.
import sys
import sqlite3
def delim(s):
delims="\"'`"
use_delim = []
for d in delims:
if d not in s:
use_delim.append(d)
return use_delim
db_name = "some.db"
db = sqlite3.connect(db_name)
mycursor = db.cursor()
table = 'so""m ][ `etable'
delimiters = delim(table)
if len(delimiters) < 1:
print "The name of the database will not allow this!"
sys.exit()
use_delimiter = delimiters[0]
print "Using delimiter ", use_delimiter
mycursor.execute('SELECT name FROM sqlite_master where (name = ?)', [table])
row = mycursor.fetchall()
valid_table = False
if row:
print (table,"table name verified")
valid_table = True
else:
print (table,"Table name not in database", db_name)
if valid_table:
try:
mycursor.execute('insert into ' +use_delimiter+ table +use_delimiter+ ' (my_data,my_column_name) values (?,?) ',(1,"Name"));
db.commit()
except Exception as e:
print "Error:", str(e)
try:
mycursor.execute('UPDATE ' +use_delimiter+ table +use_delimiter+ ' set my_column_name = ? where my_data = ?', ["ReNamed",1])
db.commit()
except Exception as e:
print "Error:", str(e)
db.close()
you can use something like this
conn = sqlite3.connect()
createTable = '''CREATE TABLE %s (# );''' %dateNow)
conn.execute(createTable)
basically, if we want to separate the data into several tables according to the date right now, for example, you want to monitor a system based on the date.
createTable = '''CREATE TABLE %s (# );''' %dateNow) means that you create a table with variable dateNow which according to your coding language, you can define dateNow as a variable to retrieve the current date from your coding language.
You can save your query in a .sql or txt file and use the open().replace() method to use variables in any part of your query. Long time reader but first time poster so I apologize if anything is off here.
```SQL in yoursql.sql```
Sel *
From yourdbschema.tablenm
```SQL to run```
tablenm = 'yourtablename'
cur = connect.cursor()
query = cur.execute(open(file = yoursql.sql).read().replace('tablenm',tablenm))
You can pass a string as the SQL command:
import sqlite3
conn = sqlite3.connect('db.db')
c = conn.cursor()
tablename, field_data = 'some_table','some_data'
query = 'SELECT * FROM '+tablename+' WHERE column1=\"'+field_data+"\""
c.execute(query)
Question: Is it possible to use a variable as your table name without having to use string constructors to do so?
Info:
I'm working on a project right now that catalogs data from a star simulation of mine. To do so I'm loading all the data into a sqlite database. It's working pretty well, but I've decided to add a lot more flexibility, efficiency, and usability to my db. I plan on later adding planetoids to the simulation, and wanted to have a table for each star. This way I wouldn't have to query a table of 20m some planetoids for the 1-4k in each solar system.
I've been told using string constructors is bad because it leaves me vulnerable to a SQL injection attack. While that isn't a big deal here as I'm the only person with access to these dbs, I would like to follow best practices. And also this way if I do a project with a similar situation where it is open to the public, I know what to do.
Currently I'm doing this:
cursor.execute("CREATE TABLE StarFrame"+self.name+" (etc etc)")
This works, but I would like to do something more like:
cursor.execute("CREATE TABLE StarFrame(?) (etc etc)",self.name)
though I understand that this would probably be impossible. though I would settle for something like
cursor.execute("CREATE TABLE (?) (etc etc)",self.name)
If this is not at all possible, I'll accept that answer, but if anyone knows a way to do this, do tell. :)
I'm coding in python.
Unfortunately, tables can't be the target of parameter substitution (I didn't find any definitive source, but I have seen it on a few web forums).
If you are worried about injection (you probably should be), you can write a function that cleans the string before passing it. Since you are looking for just a table name, you should be safe just accepting alphanumerics, stripping out all punctuation, such as )(][;, and whitespace. Basically, just keep A-Z a-z 0-9.
def scrub(table_name):
return ''.join( chr for chr in table_name if chr.isalnum() )
scrub('); drop tables --') # returns 'droptables'
For people searching for a way to make the table as a variable, I got this from another reply to same question here:
It said the following and it works. It's all quoted from mhawke:
You can't use parameter substitution for the table name. You need to add the table name to the query string yourself. Something like this:
query = 'SELECT * FROM {}'.format(table)
c.execute(query)
One thing to be mindful of is the source of the value for the table name. If that comes from an untrusted source, e.g. a user, then you need to validate the table name to avoid potential SQL injection attacks. One way might be to construct a parameterised query that looks up the table name from the DB catalogue:
import sqlite3
def exists_table(db, name):
query = "SELECT 1 FROM sqlite_master WHERE type='table' and name = ?"
return db.execute(query, (name,)).fetchone() is not None
I wouldn't separate the data into more than one table. If you create an index on the star column, you won't have any problem efficiently accessing the data.
Try with string formatting:
sql_cmd = '''CREATE TABLE {}(id, column1, column2, column2)'''.format(
'table_name')
db.execute(sql_cmd)
Replace 'table_name' with your desire.
To avoid hard-coding table names, I've used:
table = "sometable"
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS {} (
importantdate DATE,
somename VARCHAR,
)'''.format(table))
c.execute('''INSERT INTO {} VALUES (?, ?)'''.format(table),
(datetime.strftime(datetime.today(), "%Y-%m-%d"),
myname))
As has been said in the other answers, "tables can't be the target of parameter substitution" but if you find yourself in a bind where you have no option, here is a method of testing if the table name supplied is valid.
Note: I have made the table name a real pig in an attempt to cover all of the bases.
import sys
import sqlite3
def delim(s):
delims="\"'`"
use_delim = []
for d in delims:
if d not in s:
use_delim.append(d)
return use_delim
db_name = "some.db"
db = sqlite3.connect(db_name)
mycursor = db.cursor()
table = 'so""m ][ `etable'
delimiters = delim(table)
if len(delimiters) < 1:
print "The name of the database will not allow this!"
sys.exit()
use_delimiter = delimiters[0]
print "Using delimiter ", use_delimiter
mycursor.execute('SELECT name FROM sqlite_master where (name = ?)', [table])
row = mycursor.fetchall()
valid_table = False
if row:
print (table,"table name verified")
valid_table = True
else:
print (table,"Table name not in database", db_name)
if valid_table:
try:
mycursor.execute('insert into ' +use_delimiter+ table +use_delimiter+ ' (my_data,my_column_name) values (?,?) ',(1,"Name"));
db.commit()
except Exception as e:
print "Error:", str(e)
try:
mycursor.execute('UPDATE ' +use_delimiter+ table +use_delimiter+ ' set my_column_name = ? where my_data = ?', ["ReNamed",1])
db.commit()
except Exception as e:
print "Error:", str(e)
db.close()
you can use something like this
conn = sqlite3.connect()
createTable = '''CREATE TABLE %s (# );''' %dateNow)
conn.execute(createTable)
basically, if we want to separate the data into several tables according to the date right now, for example, you want to monitor a system based on the date.
createTable = '''CREATE TABLE %s (# );''' %dateNow) means that you create a table with variable dateNow which according to your coding language, you can define dateNow as a variable to retrieve the current date from your coding language.
You can save your query in a .sql or txt file and use the open().replace() method to use variables in any part of your query. Long time reader but first time poster so I apologize if anything is off here.
```SQL in yoursql.sql```
Sel *
From yourdbschema.tablenm
```SQL to run```
tablenm = 'yourtablename'
cur = connect.cursor()
query = cur.execute(open(file = yoursql.sql).read().replace('tablenm',tablenm))
You can pass a string as the SQL command:
import sqlite3
conn = sqlite3.connect('db.db')
c = conn.cursor()
tablename, field_data = 'some_table','some_data'
query = 'SELECT * FROM '+tablename+' WHERE column1=\"'+field_data+"\""
c.execute(query)