Skip to content

Commit 95e4431

Browse files
miss-islingtonErlend Egeberg Aasland
and
Erlend Egeberg Aasland
authored
bpo-20364: Improve sqlite3 placeholder docs (GH-25003)
(cherry picked from commit 3386ca0) Co-authored-by: Erlend Egeberg Aasland <[email protected]>
1 parent 57873af commit 95e4431

File tree

3 files changed

+48
-48
lines changed

3 files changed

+48
-48
lines changed

Doc/includes/sqlite3/execute_1.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,22 @@
22

33
con = sqlite3.connect(":memory:")
44
cur = con.cursor()
5-
cur.execute("create table people (name_last, age)")
6-
7-
who = "Yeltsin"
8-
age = 72
5+
cur.execute("create table lang (lang_name, lang_age)")
96

107
# This is the qmark style:
11-
cur.execute("insert into people values (?, ?)", (who, age))
8+
cur.execute("insert into lang values (?, ?)", ("C", 49))
129

13-
# And this is the named style:
14-
cur.execute("select * from people where name_last=:who and age=:age", {"who": who, "age": age})
10+
# The qmark style used with executemany():
11+
lang_list = [
12+
("Fortran", 64),
13+
("Python", 30),
14+
("Go", 11),
15+
]
16+
cur.executemany("insert into lang values (?, ?)", lang_list)
1517

16-
print(cur.fetchone())
18+
# And this is the named style:
19+
cur.execute("select * from lang where lang_name=:name and lang_age=:age",
20+
{"name": "C", "age": 49})
21+
print(cur.fetchall())
1722

1823
con.close()

Doc/library/sqlite3.rst

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -54,33 +54,6 @@ The data you've saved is persistent and is available in subsequent sessions::
5454
con = sqlite3.connect('example.db')
5555
cur = con.cursor()
5656

57-
Usually your SQL operations will need to use values from Python variables. You
58-
shouldn't assemble your query using Python's string operations because doing so
59-
is insecure; it makes your program vulnerable to an SQL injection attack
60-
(see https://xkcd.com/327/ for humorous example of what can go wrong).
61-
62-
Instead, use the DB-API's parameter substitution. Put ``?`` as a placeholder
63-
wherever you want to use a value, and then provide a tuple of values as the
64-
second argument to the cursor's :meth:`~Cursor.execute` method. (Other database
65-
modules may use a different placeholder, such as ``%s`` or ``:1``.) For
66-
example::
67-
68-
# Never do this -- insecure!
69-
symbol = 'RHAT'
70-
cur.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)
71-
72-
# Do this instead
73-
t = ('RHAT',)
74-
cur.execute('SELECT * FROM stocks WHERE symbol=?', t)
75-
print(cur.fetchone())
76-
77-
# Larger example that inserts many records at a time
78-
purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
79-
('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
80-
('2006-04-06', 'SELL', 'IBM', 500, 53.00),
81-
]
82-
cur.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases)
83-
8457
To retrieve data after executing a SELECT statement, you can either treat the
8558
cursor as an :term:`iterator`, call the cursor's :meth:`~Cursor.fetchone` method to
8659
retrieve a single matching row, or call :meth:`~Cursor.fetchall` to get a list of the
@@ -97,6 +70,33 @@ This example uses the iterator form::
9770
('2006-04-05', 'BUY', 'MSFT', 1000, 72.0)
9871

9972

73+
.. _sqlite3-placeholders:
74+
75+
Usually your SQL operations will need to use values from Python variables. You
76+
shouldn't assemble your query using Python's string operations because doing so
77+
is insecure; it makes your program vulnerable to an SQL injection attack
78+
(see the `xkcd webcomic <https://xkcd.com/327/>`_ for a humorous example of
79+
what can go wrong)::
80+
81+
# Never do this -- insecure!
82+
symbol = 'RHAT'
83+
cur.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)
84+
85+
Instead, use the DB-API's parameter substitution. Put a placeholder wherever
86+
you want to use a value, and then provide a tuple of values as the second
87+
argument to the cursor's :meth:`~Cursor.execute` method. An SQL statement may
88+
use one of two kinds of placeholders: question marks (qmark style) or named
89+
placeholders (named style). For the qmark style, ``parameters`` must be a
90+
:term:`sequence <sequence>`. For the named style, it can be either a
91+
:term:`sequence <sequence>` or :class:`dict` instance. The length of the
92+
:term:`sequence <sequence>` must match the number of placeholders, or a
93+
:exc:`ProgrammingError` is raised. If a :class:`dict` is given, it must contain
94+
keys for all named parameters. Any extra items are ignored. Here's an example
95+
of both styles:
96+
97+
.. literalinclude:: ../includes/sqlite3/execute_1.py
98+
99+
100100
.. seealso::
101101

102102
https://www.sqlite.org
@@ -608,14 +608,8 @@ Cursor Objects
608608

609609
.. method:: execute(sql[, parameters])
610610

611-
Executes an SQL statement. The SQL statement may be parameterized (i. e.
612-
placeholders instead of SQL literals). The :mod:`sqlite3` module supports two
613-
kinds of placeholders: question marks (qmark style) and named placeholders
614-
(named style).
615-
616-
Here's an example of both styles:
617-
618-
.. literalinclude:: ../includes/sqlite3/execute_1.py
611+
Executes an SQL statement. Values may be bound to the statement using
612+
:ref:`placeholders <sqlite3-placeholders>`.
619613

620614
:meth:`execute` will only execute a single SQL statement. If you try to execute
621615
more than one statement with it, it will raise a :exc:`.Warning`. Use
@@ -625,9 +619,10 @@ Cursor Objects
625619

626620
.. method:: executemany(sql, seq_of_parameters)
627621

628-
Executes an SQL command against all parameter sequences or mappings found in
629-
the sequence *seq_of_parameters*. The :mod:`sqlite3` module also allows
630-
using an :term:`iterator` yielding parameters instead of a sequence.
622+
Executes a :ref:`parameterized <sqlite3-placeholders>` SQL command
623+
against all parameter sequences or mappings found in the sequence
624+
*seq_of_parameters*. The :mod:`sqlite3` module also allows using an
625+
:term:`iterator` yielding parameters instead of a sequence.
631626

632627
.. literalinclude:: ../includes/sqlite3/executemany_1.py
633628

Doc/tools/susp-ignored.csv

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,9 @@ library/smtplib,,:port,method must support that as well as a regular host:port
211211
library/socket,,::,'5aef:2b::8'
212212
library/socket,,:can,"return (can_id, can_dlc, data[:can_dlc])"
213213
library/socket,,:len,fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
214-
library/sqlite3,,:age,"cur.execute(""select * from people where name_last=:who and age=:age"", {""who"": who, ""age"": age})"
214+
library/sqlite3,,:name,"cur.execute(""select * from lang where lang_name=:name and lang_age=:age"","
215+
library/sqlite3,,:age,"cur.execute(""select * from lang where lang_name=:name and lang_age=:age"","
215216
library/sqlite3,,:memory,
216-
library/sqlite3,,:who,"cur.execute(""select * from people where name_last=:who and age=:age"", {""who"": who, ""age"": age})"
217217
library/sqlite3,,:path,"db = sqlite3.connect('file:path/to/database?mode=ro', uri=True)"
218218
library/ssl,,:My,"Organizational Unit Name (eg, section) []:My Group"
219219
library/ssl,,:My,"Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Organization, Inc."

0 commit comments

Comments
 (0)