More doc love for the sql module

This commit is contained in:
Daniele Varrazzo 2017-01-01 18:04:51 +01:00
parent 9926942260
commit 49461c2c39
4 changed files with 63 additions and 24 deletions

View File

@ -151,6 +151,24 @@ Psycopg converts :sql:`json` values into Python objects but :sql:`jsonb` values
See :ref:`adapt-json` for further details.
.. _faq-identifier:
.. cssclass:: faq
How can I pass field/table names to a query?
The arguments in the `~cursor.execute()` methods can only represent data
to pass to the query: they cannot represent a table or field name::
# This doesn't work
cur.execute("insert into %s values (%s)", ["my_table", 42])
If you want to build a query dynamically you can use the objects exposed
by the `psycopg2.sql` module::
cur.execute(
sql.SQL("insert into %s values (%%s)") % [sql.Identifier("my_table")],
[42])
.. _faq-bytea-9.0:
.. cssclass:: faq

View File

@ -9,7 +9,7 @@
The module contains objects and functions useful to generate SQL dynamically,
in a convenient and safe way. SQL identifiers (e.g. names of tables and
fields) cannot be passed to the `~cursor.execute()` function like query
fields) cannot be passed to the `~cursor.execute()` method like query
arguments::
# This will not work
@ -45,7 +45,7 @@ in the presence of a table or field name with containing characters to escape,
or will present a potentially exploitable weakness.
The objects exposed by the `!psycopg2.sql` module allow generating SQL
statements on the fly, separating clearly the variable parts in the statement
statements on the fly, separating clearly the variable parts of the statement
from the query parameters::
from psycopg2 import sql

View File

@ -132,9 +132,10 @@ query:
>>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar",)) # correct
>>> cur.execute("INSERT INTO foo VALUES (%s)", ["bar"]) # correct
- Only variable values should be bound via this method: it shouldn't be used
to set table or field names. For these elements, ordinary string formatting
should be used before running `~cursor.execute()`.
- Only query values should be bound via this method: it shouldn't be used to
merge table or field names to the query. If you need to generate dynamically
an SQL query (for instance choosing dynamically a table name) you can use
the facilities provided by the `psycopg2.sql` module.

View File

@ -78,9 +78,10 @@ class Composed(Composable):
Example::
>>> sql.Composed([sql.SQL("insert into "), sql.Identifier("table")]) \\
... .as_string(conn)
'insert into "table"'
>>> comp = sql.Composed(
... [sql.SQL("insert into "), sql.Identifier("table")])
>>> print(comp.as_string(conn))
insert into "table"
.. automethod:: join
"""
@ -119,8 +120,8 @@ class Composed(Composable):
Example::
>>> fields = sql.Identifier('foo') + sql.Identifier('bar') # a Composed
>>> fields.join(', ').as_string(conn)
'"foo", "bar"'
>>> print(fields.join(', ').as_string(conn))
"foo", "bar"
"""
if isinstance(joiner, basestring):
@ -155,9 +156,8 @@ class SQL(Composable):
>>> query = sql.SQL("select %s from %s") % [
... sql.SQL(', ').join([sql.Identifier('foo'), sql.Identifier('bar')]),
... sql.Identifier('table')]
>>> query.as_string(conn)
select "foo", "bar" from "table"'
>>> print(query.as_string(conn))
select "foo", "bar" from "table"
.. automethod:: join
"""
@ -184,8 +184,8 @@ class SQL(Composable):
Example::
>>> snip - sql.SQL(', ').join(map(sql.Identifier, ['foo', 'bar', 'baz']))
>>> snip.as_string(conn)
'"foo", "bar", "baz"'
>>> print(snip.as_string(conn))
"foo", "bar", "baz"
"""
if isinstance(seq, Composed):
seq = seq._seq
@ -214,6 +214,15 @@ class Identifier(Composable):
.. __: https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html# \
SQL-SYNTAX-IDENTIFIERS
Example::
>>> t1 = sql.Identifier("foo")
>>> t2 = sql.Identifier("ba'r")
>>> t3 = sql.Identifier('ba"z')
>>> print(sql.SQL(', ').join([t1, t2, t3]).as_string(conn))
"foo", "ba'r", "ba""z"
"""
def __init__(self, string):
if not isinstance(string, basestring):
@ -239,6 +248,14 @@ class Literal(Composable):
The string returned by `!as_string()` follows the normal :ref:`adaptation
rules <python-types-adaptation>` for Python objects.
Example::
>>> s1 = sql.Literal("foo")
>>> s2 = sql.Literal("ba'r")
>>> s3 = sql.Literal(42)
>>> print(sql.SQL(', ').join([s1, s2, s3]).as_string(conn))
'foo', 'ba''r', 42
"""
def __init__(self, wrapped):
self._wrapped = wrapped
@ -277,17 +294,20 @@ class Placeholder(Composable):
Examples::
>>> (sql.SQL("insert into table (%s) values (%s)") % [
... sql.SQL(', ').join(map(sql.Identifier, names)),
... sql.SQL(', ').join(sql.Placeholder() * 3)
... ]).as_string(conn)
'insert into table ("foo", "bar", "baz") values (%s, %s, %s)'
>>> names = ['foo', 'bar', 'baz']
>>> (sql.SQL("insert into table (%s) values (%s)") % [
>>> q1 = sql.SQL("insert into table (%s) values (%s)") % [
... sql.SQL(', ').join(map(sql.Identifier, names)),
... sql.SQL(', ').join(map(sql.Placeholder, names))
... ]).as_string(conn)
'insert into table ("foo", "bar", "baz") values (%(foo)s, %(bar)s, %(baz)s)'
... sql.SQL(', ').join(sql.Placeholder() * 3)]
>>> print(q1.as_string(conn))
insert into table ("foo", "bar", "baz") values (%s, %s, %s)
>>> q2 = sql.SQL("insert into table (%s) values (%s)") % [
... sql.SQL(', ').join(map(sql.Identifier, names)),
... sql.SQL(', ').join(map(sql.Placeholder, names))]
>>> print(q2.as_string(conn))
insert into table ("foo", "bar", "baz") values (%(foo)s, %(bar)s, %(baz)s)
"""
def __init__(self, name=None):