Jak używać zmiennych w instrukcji SQL w Pythonie?

94

Ok, więc nie mam takiego doświadczenia w Pythonie.

Mam następujący kod w Pythonie:

cursor.execute("INSERT INTO table VALUES var1, var2, var3,")

gdzie var1jest liczbą całkowitą, var2i var3są łańcuchami.

Jak mogę napisać nazwy zmiennych bez Pythona, włączając je jako część tekstu zapytania?

user111606
źródło

Odpowiedzi:

107
cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

Zwróć uwagę, że parametry są przekazywane jako krotka.

Interfejs API bazy danych wykonuje poprawne ucieczki i cytowanie zmiennych. Uważaj, aby nie używać operatora formatowania ciągu ( %), ponieważ

  1. nie stosuje żadnych znaków ucieczki ani cytowania.
  2. jest podatny na ataki w niekontrolowanym formacie string, np . iniekcja SQL .
Ayman Hourieh
źródło
Ciekawe, dlaczego to działa ze zmiennymi oddzielnie zamiast w tablicy (var1, var2, var3)?
Andomar
Zgodnie ze specyfikacją DB API wygląda na to, że może to być w obie strony: python.org/dev/peps/pep-0249
Ayman Hourieh
9
@thekashyap Przeczytaj ponownie uważnie. Niezabezpieczone jest użycie operatora formatowania ciągu %. W rzeczywistości tak mówię w odpowiedzi.
Ayman Hourieh
mój błąd .. Wyobraziłem sobie, że % zamiast ,między ciągiem a zmiennymi .. nie mogę cofnąć mojego głosowania z różnych powodów .. Osobiście chciałbym zobaczyć słowa takie jak niepewny / atak itp. wymienione w opisie, w którym mówisz nie 't use %..
Kashyap,
1
@eric odpowiedź mówi, że nie używaj % operatora do formatowania ciągu. Te %w łańcuchu są używane cursor.executebezpośrednio, a ponieważ wie, że generuje kod SQL, może zrobić więcej, aby Cię chronić.
Mark Ransom
69

Różne implementacje Python DB-API mogą używać różnych symboli zastępczych, więc musisz dowiedzieć się, którego z nich używasz - może to być (np. Z MySQLdb):

cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

lub (np. z sqlite3 ze standardowej biblioteki Pythona):

cursor.execute("INSERT INTO table VALUES (?, ?, ?)", (var1, var2, var3))

lub jeszcze inne (po VALUEStym, jak mogłeś mieć (:1, :2, :3)lub „nazwane style” (:fee, :fie, :fo)lub gdy (%(fee)s, %(fie)s, %(fo)s)przekazujesz dykt zamiast mapy jako drugi argument execute). Sprawdź paramstylestałą łańcuchową w module DB API, którego używasz, i poszukaj paramstyle na http://www.python.org/dev/peps/pep-0249/, aby zobaczyć, jakie są wszystkie style przekazywania parametrów!

Alex Martelli
źródło
Czy można zrobić to samo, ale z zewnętrznym skryptem SQL?
Novitoll
48

Wiele sposobów. NIE używaj najbardziej oczywistego ( %sz %) w prawdziwym kodzie, jest on otwarty na ataki .

Tutaj skopiuj i wklej z pydoc z sqlite3 :

# Never do this -- insecure!
symbol = 'RHAT'
c.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)

# Do this instead
t = ('RHAT',)
c.execute('SELECT * FROM stocks WHERE symbol=?', t)
print c.fetchone()

# Larger example that inserts many records at a time
purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
             ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
             ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
            ]
c.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases)

Więcej przykładów, jeśli potrzebujesz:

# Multiple values single statement/execution
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', ('RHAT', 'MSO'))
print c.fetchall()
c.execute('SELECT * FROM stocks WHERE symbol IN (?, ?)', ('RHAT', 'MSO'))
print c.fetchall()
# This also works, though ones above are better as a habit as it's inline with syntax of executemany().. but your choice.
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', 'RHAT', 'MSO')
print c.fetchall()
# Insert a single item
c.execute('INSERT INTO stocks VALUES (?,?,?,?,?)', ('2006-03-28', 'BUY', 'IBM', 1000, 45.00))
Kashyap
źródło
4
Niektóre implementacje DB-API faktycznie używają% s do swoich zmiennych - w szczególności psycopg2 dla PostgreSQL. Nie należy tego mylić (choć łatwo jest) z użyciem% s z operatorem% do zamiany łańcucha. Byłoby naprawdę miło, gdybyśmy ze względu na przenośność mogli mieć zdefiniowany standardowy sposób określania parametrów SQL dla DB-API.
ThatAintWorking
26

http://www.amk.ca/python/writing/DB-API.html

Zachowaj ostrożność, gdy po prostu dołączasz wartości zmiennych do swoich instrukcji: Wyobraź sobie użytkownika nazywającego siebie ';DROP TABLE Users;'- dlatego musisz użyć funkcji ucieczki sql, którą Python zapewnia ci, gdy używasz kursor.execute w przyzwoity sposób. Przykład w adresie URL to:

cursor.execute("insert into Attendees values (?, ?, ?)", (name,
seminar, paid) )
Numlock
źródło
13
W rzeczywistości nie jest to ucieczka SQL. To zmienne wiązanie, które jest znacznie prostsze i bardziej bezpośrednie. Wartości są wiązane z instrukcją SQL po przeanalizowaniu, dzięki czemu jest odporna na atak iniekcyjny.
S.Lott,
cóż, to, czy jest to ucieczka SQL, czy powiązanie zmiennej, zależy od tego, jak dobry lub zły jest twój serwer bazy danych / sterownik DB-API. Widziałem rzeczywiste, szeroko rozpowszechnione produkcyjne bazy danych, w których ich sterownik DB-API po prostu ucieka, zamiast utrzymywać dane i kod poza pasmem. Nie trzeba dodawać, że nie mam wielkiego szacunku do tych tak zwanych „baz danych”.
Charles Duffy