diff --git a/doc/src/sql.rst b/doc/src/sql.rst new file mode 100644 index 00000000..1328a867 --- /dev/null +++ b/doc/src/sql.rst @@ -0,0 +1,68 @@ +`psycopg2.sql` -- SQL string composition +======================================== + +.. sectionauthor:: Daniele Varrazzo + +.. module:: psycopg2.sql + +.. versionadded:: 2.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 +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 `~psycopg2.extensions.quote_ident()`:: + + # This works, but it is not optimal + table_name = 'my_table' + cur.execute( + "insert into %s values (%%s, %%s)" % ext.quote_ident(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 (`~psycopg2.extensions.adapt()` should be +used...). It is also still relatively dangerous: if `!quote_ident()` 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 `!psycopg2.sql` module allow generating SQL +statements on the fly, separating clearly the variable parts in the statement +from the query parameters:: + + from psycopg2 import sql + + cur.execute( + sql.SQL("insert into %s values (%%s, %%s)") % [sql.Identifier('my_table')], + [10, 20]) + + +.. autoclass:: Composable + +.. autoclass:: SQL + +.. autoclass:: Identifier + +.. autoclass:: Literal + +.. autoclass:: Placeholder + +.. autoclass:: Composed