summaryrefslogtreecommitdiffstats
path: root/docs/api/sql.rst
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 17:41:08 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 17:41:08 +0000
commit506ed8899b3a97e512be3fd6d44d5b11463bf9bf (patch)
tree808913770c5e6935d3714058c2a066c57b4632ec /docs/api/sql.rst
parentInitial commit. (diff)
downloadpsycopg3-506ed8899b3a97e512be3fd6d44d5b11463bf9bf.tar.xz
psycopg3-506ed8899b3a97e512be3fd6d44d5b11463bf9bf.zip
Adding upstream version 3.1.7.upstream/3.1.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'docs/api/sql.rst')
-rw-r--r--docs/api/sql.rst151
1 files changed, 151 insertions, 0 deletions
diff --git a/docs/api/sql.rst b/docs/api/sql.rst
new file mode 100644
index 0000000..6959fee
--- /dev/null
+++ b/docs/api/sql.rst
@@ -0,0 +1,151 @@
+`sql` -- SQL string composition
+===============================
+
+.. index::
+ double: Binding; Client-Side
+
+.. module:: psycopg.sql
+
+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 `~psycopg.Cursor.execute()` method like query
+arguments::
+
+ # This will not work
+ table_name = 'my_table'
+ cur.execute("INSERT INTO %s VALUES (%s, %s)", [table_name, 10, 20])
+
+The SQL query should be composed before the arguments are merged, for
+instance::
+
+ # This works, but it is not optimal
+ table_name = 'my_table'
+ cur.execute(
+ "INSERT INTO %s VALUES (%%s, %%s)" % table_name,
+ [10, 20])
+
+This sort of works, but it is an accident waiting to happen: the table name
+may be an invalid SQL literal and need quoting; even more serious is the
+security problem in case the table name comes from an untrusted source. The
+name should be escaped using `~psycopg.pq.Escaping.escape_identifier()`::
+
+ from psycopg.pq import Escaping
+
+ # This works, but it is not optimal
+ table_name = 'my_table'
+ cur.execute(
+ "INSERT INTO %s VALUES (%%s, %%s)" % Escaping.escape_identifier(table_name),
+ [10, 20])
+
+This is now safe, but it somewhat ad-hoc. In case, for some reason, it is
+necessary to include a value in the query string (as opposite as in a value)
+the merging rule is still different. It is also still relatively dangerous: if
+`!escape_identifier()` is forgotten somewhere, the program will usually work,
+but will eventually crash 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 `!psycopg.sql` module allow generating SQL
+statements on the fly, separating clearly the variable parts of the statement
+from the query parameters::
+
+ from psycopg import sql
+
+ cur.execute(
+ sql.SQL("INSERT INTO {} VALUES (%s, %s)")
+ .format(sql.Identifier('my_table')),
+ [10, 20])
+
+
+Module usage
+------------
+
+Usually you should express the template of your query as an `SQL` instance
+with ``{}``\-style placeholders and use `~SQL.format()` to merge the variable
+parts into them, all of which must be `Composable` subclasses. You can still
+have ``%s``\-style placeholders in your query and pass values to
+`~psycopg.Cursor.execute()`: such value placeholders will be untouched by
+`!format()`::
+
+ query = sql.SQL("SELECT {field} FROM {table} WHERE {pkey} = %s").format(
+ field=sql.Identifier('my_name'),
+ table=sql.Identifier('some_table'),
+ pkey=sql.Identifier('id'))
+
+The resulting object is meant to be passed directly to cursor methods such as
+`~psycopg.Cursor.execute()`, `~psycopg.Cursor.executemany()`,
+`~psycopg.Cursor.copy()`, but can also be used to compose a query as a Python
+string, using the `~Composable.as_string()` method::
+
+ cur.execute(query, (42,))
+ full_query = query.as_string(cur)
+
+If part of your query is a variable sequence of arguments, such as a
+comma-separated list of field names, you can use the `SQL.join()` method to
+pass them to the query::
+
+ query = sql.SQL("SELECT {fields} FROM {table}").format(
+ fields=sql.SQL(',').join([
+ sql.Identifier('field1'),
+ sql.Identifier('field2'),
+ sql.Identifier('field3'),
+ ]),
+ table=sql.Identifier('some_table'))
+
+
+`!sql` objects
+--------------
+
+The `!sql` objects are in the following inheritance hierarchy:
+
+| `Composable`: the base class exposing the common interface
+| ``|__`` `SQL`: a literal snippet of an SQL query
+| ``|__`` `Identifier`: a PostgreSQL identifier or dot-separated sequence of identifiers
+| ``|__`` `Literal`: a value hardcoded into a query
+| ``|__`` `Placeholder`: a `%s`\ -style placeholder whose value will be added later e.g. by `~psycopg.Cursor.execute()`
+| ``|__`` `Composed`: a sequence of `!Composable` instances.
+
+
+.. autoclass:: Composable()
+
+ .. automethod:: as_bytes
+ .. automethod:: as_string
+
+
+.. autoclass:: SQL
+
+ .. versionchanged:: 3.1
+
+ The input object should be a `~typing.LiteralString`. See :pep:`675`
+ for details.
+
+ .. automethod:: format
+
+ .. automethod:: join
+
+
+.. autoclass:: Identifier
+
+.. autoclass:: Literal
+
+ .. versionchanged:: 3.1
+ Add a type cast to the representation if useful in ambiguous context
+ (e.g. ``'2000-01-01'::date``)
+
+.. autoclass:: Placeholder
+
+.. autoclass:: Composed
+
+ .. automethod:: join
+
+
+Utility functions
+-----------------
+
+.. autofunction:: quote
+
+.. data::
+ NULL
+ DEFAULT
+
+ `sql.SQL` objects often useful in queries.