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. 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: .. _faq-bytea-9.0:
.. cssclass:: faq .. cssclass:: faq

View File

@ -9,7 +9,7 @@
The module contains objects and functions useful to generate SQL dynamically, 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 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:: arguments::
# This will not work # 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. or will present a potentially exploitable weakness.
The objects exposed by the `!psycopg2.sql` module allow generating SQL 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 the query parameters::
from psycopg2 import sql 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
>>> 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 - Only query values should be bound via this method: it shouldn't be used to
to set table or field names. For these elements, ordinary string formatting merge table or field names to the query. If you need to generate dynamically
should be used before running `~cursor.execute()`. 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:: Example::
>>> sql.Composed([sql.SQL("insert into "), sql.Identifier("table")]) \\ >>> comp = sql.Composed(
... .as_string(conn) ... [sql.SQL("insert into "), sql.Identifier("table")])
'insert into "table"' >>> print(comp.as_string(conn))
insert into "table"
.. automethod:: join .. automethod:: join
""" """
@ -119,8 +120,8 @@ class Composed(Composable):
Example:: Example::
>>> fields = sql.Identifier('foo') + sql.Identifier('bar') # a Composed >>> fields = sql.Identifier('foo') + sql.Identifier('bar') # a Composed
>>> fields.join(', ').as_string(conn) >>> print(fields.join(', ').as_string(conn))
'"foo", "bar"' "foo", "bar"
""" """
if isinstance(joiner, basestring): if isinstance(joiner, basestring):
@ -155,9 +156,8 @@ class SQL(Composable):
>>> query = sql.SQL("select %s from %s") % [ >>> query = sql.SQL("select %s from %s") % [
... sql.SQL(', ').join([sql.Identifier('foo'), sql.Identifier('bar')]), ... sql.SQL(', ').join([sql.Identifier('foo'), sql.Identifier('bar')]),
... sql.Identifier('table')] ... sql.Identifier('table')]
>>> query.as_string(conn) >>> print(query.as_string(conn))
select "foo", "bar" from "table"' select "foo", "bar" from "table"
.. automethod:: join .. automethod:: join
""" """
@ -184,8 +184,8 @@ class SQL(Composable):
Example:: Example::
>>> snip - sql.SQL(', ').join(map(sql.Identifier, ['foo', 'bar', 'baz'])) >>> snip - sql.SQL(', ').join(map(sql.Identifier, ['foo', 'bar', 'baz']))
>>> snip.as_string(conn) >>> print(snip.as_string(conn))
'"foo", "bar", "baz"' "foo", "bar", "baz"
""" """
if isinstance(seq, Composed): if isinstance(seq, Composed):
seq = seq._seq seq = seq._seq
@ -214,6 +214,15 @@ class Identifier(Composable):
.. __: https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html# \ .. __: https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html# \
SQL-SYNTAX-IDENTIFIERS 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): def __init__(self, string):
if not isinstance(string, basestring): if not isinstance(string, basestring):
@ -239,6 +248,14 @@ class Literal(Composable):
The string returned by `!as_string()` follows the normal :ref:`adaptation The string returned by `!as_string()` follows the normal :ref:`adaptation
rules <python-types-adaptation>` for Python objects. 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): def __init__(self, wrapped):
self._wrapped = wrapped self._wrapped = wrapped
@ -277,17 +294,20 @@ class Placeholder(Composable):
Examples:: Examples::
>>> (sql.SQL("insert into table (%s) values (%s)") % [ >>> names = ['foo', 'bar', 'baz']
... 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)'
>>> (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.Identifier, names)),
... sql.SQL(', ').join(map(sql.Placeholder, names)) ... sql.SQL(', ').join(sql.Placeholder() * 3)]
... ]).as_string(conn) >>> print(q1.as_string(conn))
'insert into table ("foo", "bar", "baz") values (%(foo)s, %(bar)s, %(baz)s)' 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): def __init__(self, name=None):