summaryrefslogtreecommitdiff
path: root/doc/build/faq
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2019-04-04 21:43:12 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2019-04-04 21:44:49 -0400
commit6845be0927245e47c27f8e160472cf9a55a41dc4 (patch)
treecf64489de6a6f9138f39bb1e1d7ab769b7a7cfef /doc/build/faq
parent97d4d15fde7999eba29c9708b65e11d82623f686 (diff)
downloadsqlalchemy-6845be0927245e47c27f8e160472cf9a55a41dc4.tar.gz
Enhance documentation for string compilation use cases
- Add a web link for UnsupportedCompilationError - Add new section to errors.rst - add more detail and cross-linking to the FAQ - include security caveats for parameter rendering Fixes: #4595 Change-Id: I31ea57c18d65770cd2a51276bbe2847a9eb72bba
Diffstat (limited to 'doc/build/faq')
-rw-r--r--doc/build/faq/sqlexpressions.rst71
1 files changed, 57 insertions, 14 deletions
diff --git a/doc/build/faq/sqlexpressions.rst b/doc/build/faq/sqlexpressions.rst
index 2571c431c..6a52e747d 100644
--- a/doc/build/faq/sqlexpressions.rst
+++ b/doc/build/faq/sqlexpressions.rst
@@ -11,19 +11,52 @@ SQL Expressions
How do I render SQL expressions as strings, possibly with bound parameters inlined?
------------------------------------------------------------------------------------
-The "stringification" of a SQLAlchemy statement or Query in the vast majority
-of cases is as simple as::
-
- print(str(statement))
-
-this applies both to an ORM :class:`~.orm.query.Query` as well as any :func:`.select` or other
-statement. Additionally, to get the statement as compiled to a
-specific dialect or engine, if the statement itself is not already
-bound to one you can pass this in to :meth:`.ClauseElement.compile`::
-
- print(statement.compile(someengine))
-
-or without an :class:`.Engine`::
+The "stringification" of a SQLAlchemy Core statement object or
+expression fragment, as well as that of an ORM :class:`.Query` object,
+in the majority of simple cases is as simple as using
+the ``str()`` builtin function, as below when use it with the ``print``
+function (note the Python ``print`` function also calls ``str()`` automatically
+if we don't use it explicitly)::
+
+ >>> from sqlalchemy import table, column, select
+ >>> t = table('my_table', column('x'))
+ >>> statement = select([t])
+ >>> print(str(statement))
+ SELECT my_table.x
+ FROM my_table
+
+The ``str()`` builtin, or an equivalent, can be invoked on ORM
+:class:`.Query` object as well as any statement such as that of
+:func:`.select`, :func:`.insert` etc. and also any expression fragment, such
+as::
+
+ >>> from sqlalchemy import column
+ >>> print(column('x') == 'some value')
+ x = :x_1
+
+Stringifying for Specific Databases
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A complication arises when the statement or fragment we are stringifying
+contains elements that have a database-specific string format, or when it
+contains elements that are only available within a certain kind of database.
+In these cases, we might get a stringified statement that is not in the correct
+syntax for the database we are targeting, or the operation may raise a
+:class:`.UnsupportedCompilationError` exception. In these cases, it is
+necessary that we stringify the statement using the
+:meth:`.ClauseElement.compile` method, while passing along an :class:`.Engine`
+or :class:`.Dialect` object that represents the target database. Such as
+below, if we have a MySQL database engine, we can stringify a statement in
+terms of the MySQL dialect::
+
+ from sqlalchemy import create_engine
+
+ engine = create_engine("mysql+pymysql://scott:tiger@localhost/test")
+ print(statement.compile(engine))
+
+More directly, without building up an :class:`.Engine` object we can
+instantiate a :class:`.Dialect` object directly, as below where we
+use a PostgreSQL dialect::
from sqlalchemy.dialects import postgresql
print(statement.compile(dialect=postgresql.dialect()))
@@ -36,6 +69,16 @@ accessor first::
statement = query.statement
print(statement.compile(someengine))
+Rendering Bound Parameters Inline
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. warning:: **Never** use this technique with string content received from
+ untrusted input, such as from web forms or other user-input applications.
+ SQLAlchemy's facilities to coerce Python values into direct SQL string
+ values are **not secure against untrusted input and do not validate the type
+ of data being passed**. Always use bound parameters when programmatically
+ invoking non-DDL SQL statements against a relational database.
+
The above forms will render the SQL statement as it is passed to the Python
:term:`DBAPI`, which includes that bound parameters are not rendered inline.
SQLAlchemy normally does not stringify bound parameters, as this is handled
@@ -52,7 +95,7 @@ flag, passed to ``compile_kwargs``::
s = select([t]).where(t.c.x == 5)
- print(s.compile(compile_kwargs={"literal_binds": True}))
+ print(s.compile(compile_kwargs={"literal_binds": True})) # **do not use** with untrusted input!!!
the above approach has the caveats that it is only supported for basic
types, such as ints and strings, and furthermore if a :func:`.bindparam`