diff options
author | Daniele Varrazzo <daniele.varrazzo@gmail.com> | 2010-02-10 04:03:30 +0000 |
---|---|---|
committer | Federico Di Gregorio <fog@initd.org> | 2010-02-14 00:39:48 +0100 |
commit | 5491dd8db03b309d65fcb58504eb20bf7c231c70 (patch) | |
tree | 860477e07431f2b4946e0853d4778471534a9092 | |
parent | 74403ff5a8508bc9f12f016ce5ef251684398dc4 (diff) | |
download | psycopg2-5491dd8db03b309d65fcb58504eb20bf7c231c70.tar.gz |
Added documentation to type casting objects.
-rw-r--r-- | doc/advanced.rst | 60 | ||||
-rw-r--r-- | doc/extensions.rst | 170 | ||||
-rw-r--r-- | doc/usage.rst | 23 |
3 files changed, 212 insertions, 41 deletions
diff --git a/doc/advanced.rst b/doc/advanced.rst index 4460ce3..fca08c7 100644 --- a/doc/advanced.rst +++ b/doc/advanced.rst @@ -49,7 +49,7 @@ An example of cursor subclass performing logging is:: single: Objects; Creating new adapters single: Adaptation; Creating new adapters single: Data types; Creating new adapters - + .. _adapting-new-types: Adapting new Python types to SQL syntax @@ -61,34 +61,38 @@ by the :func:`psycopg2.extensions.adapt()` function. The :meth:`cursor.execute()` method adapts its arguments to the :class:`psycopg2.extensions.ISQLQuote` protocol. Objects that conform to this -protocol expose a ``getquoted()`` method returning the SQL representation of -the object as a string. +protocol expose a :meth:`getquoted()` method returning the SQL representation +of the object as a string. The easiest way to adapt an object to an SQL string is to register an adapter function via the :func:`psycopg2.extensions.register_adapter()` function. The adapter function must take the value to be adapted as argument and return a conform object. A convenient object is the :func:`psycopg2.extensions.AsIs` -wrapper, whose ``getquoted()`` result is simply the ``str()``\ ingification of -the wrapped object. +wrapper, whose :meth:`getquoted()` result is simply the ``str()``\ ing +conversion of the wrapped object. -Example: mapping of a ``Point`` class into the ``point`` PostgreSQL geometric -type:: +Example: mapping of a :data:`Point` class into the |point|_ PostgreSQL +geometric type:: from psycopg2.extensions import adapt, register_adapter, AsIs - + class Point(object): def __init__(self, x, y): self.x = x self.y = y - + def adapt_point(point): return AsIs("'(%s, %s)'" % (adapt(point.x), adapt(point.y))) - + register_adapter(Point, adapt_point) - - curs.execute("INSERT INTO atable (apoint) VALUES (%s)", + + curs.execute("INSERT INTO atable (apoint) VALUES (%s)", (Point(1.23, 4.56),)) + +.. |point| replace:: ``point`` +.. _point: http://www.postgresql.org/docs/8.4/static/datatype-geometric.html#AEN6084 + The above function call results in the SQL command:: INSERT INTO atable (apoint) VALUES ((1.23, 4.56)); @@ -99,8 +103,8 @@ The above function call results in the SQL command:: .. _type-casting-from-sql-to-python: -Type casting of SQL types into Python values --------------------------------------------- +Type casting of SQL types into Python objects +--------------------------------------------- PostgreSQL objects read from the database can be adapted to Python objects through an user-defined adapting function. An adapter function takes two @@ -111,13 +115,13 @@ previously defined ``Point`` class:: def cast_point(value, curs): if value is not None: - # Convert from (f1, f2) syntax using a regular expression. - m = re.match("\((.*),(.*)\)", value) + # Convert from (f1, f2) syntax using a regular expression. + m = re.match(r"\(([^)]+),([^)]+)\)", value) if m: return Point(float(m.group(1)), float(m.group(2))) - + To create a mapping from the PostgreSQL type (either standard or user-defined), -its ``oid`` must be known. It can be retrieved either by the second column of +its OID must be known. It can be retrieved either by the second column of the cursor description:: curs.execute("SELECT NULL::point") @@ -132,11 +136,11 @@ namespace for system objects is ``pg_catalog``):: ON typnamespace = pg_namespace.oid WHERE typname = %(typename)s AND nspname = %(namespace)s""", - {'typename': 'point', 'namespace': 'pg_catalog'}) - + {'typename': 'point', 'namespace': 'pg_catalog'}) + point_oid = curs.fetchone()[0] -After you know the object ``oid``, you must can and register the new type:: +After you know the object OID, you must can and register the new type:: POINT = psycopg2.extensions.new_type((point_oid,), "POINT", cast_point) psycopg2.extensions.register_type(POINT) @@ -144,7 +148,7 @@ After you know the object ``oid``, you must can and register the new type:: The :func:`psycopg2.extensions.new_type()` function binds the object oids (more than one can be specified) to the adapter function. :func:`psycopg2.extensions.register_type()` completes the spell. Conversion -is automatically performed when a column whose type is a registered ``oid`` is +is automatically performed when a column whose type is a registered OID is read:: >>> curs.execute("SELECT '(10.2,20.3)'::point") @@ -217,7 +221,7 @@ Running the script and executing the command ``NOTIFY test`` in a separate .. index:: double: Asynchronous; Query - + .. _asynchronous-queries: Asynchronous queries @@ -237,7 +241,7 @@ simple example, from the connection to the query:: conn = psycopg2.connect(database='test') curs = conn.cursor() curs.execute("SELECT * from test WHERE fielda > %s", (1971,), async=1) - + From then on any query on other cursors derived from the same connection is doomed to fail (and raise an exception) until the original cursor (the one executing the query) complete the asynchronous operation. This can happen in @@ -245,12 +249,12 @@ a number of different ways: 1) one of the :obj:`.fetch*()` methods is called, effectively blocking until data has been sent from the backend to the client, terminating the query. - + 2) :meth:`connection.cancel` is called. This method tries to abort the current query and will block until the query is aborted or fully executed. The return value is ``True`` if the query was successfully aborted or ``False`` if it was executed. Query result are discarded in both cases. - + 3) :meth:`cursor.execute` is called again on the same cursor (:obj:`.execute()` on a different cursor will simply raise an exception). This waits for the complete execution of the current query, discard any @@ -271,13 +275,13 @@ asynchronous queries: :meth:`cursor.isready` Returns ``False`` if the backend is still processing the query or ``True`` if data is ready to be fetched (by one of the :obj:`.fetch*()` methods). - + A code snippet that shows how to use the cursor object in a :func:`select()` call:: import psycopg2 import select - + conn = psycopg2.connect(database='test') curs = conn.cursor() curs.execute("SELECT * from test WHERE fielda > %s", (1971,), async=1) diff --git a/doc/extensions.rst b/doc/extensions.rst index e90ebbf..b9b298c 100644 --- a/doc/extensions.rst +++ b/doc/extensions.rst @@ -36,7 +36,167 @@ functionalities defined by the |DBAPI|. .. todo:: class lobject -.. todo:: finish module extensions + +.. _sql-adaptation-objects: + +SQL adaptation protocol objects +------------------------------- + +Psycopg provides a flexible system to adapt Python objects to the SQL syntax +(inspired to the :pep:`246`), allowing serialization in PostgreSQL. See +:ref:`adapting-new-types` for a detailed description. The following objects +deal with Python objects adaptation: + +.. function:: adapt(obj) + + Return the SQL representation of :obj:`obj` as a string. Raise a + :exc:`ProgrammingError` if how to adapt the object is unknown. In order + to allow new objects to be adapted, register a new adapter for it using + the :func:`register_adapter` function. + + The function is the entry point of the adaptation mechanism: it can be + used to write adapters for complex objects by recursively calling + :func:`adapt` on its components. + +.. function:: register_adapter(class, adapter) + + Register a new adapter for the objects of class :data:`class`. + + :data:`adapter` should be a function taking a single argument (the object + to adapt) and returning an object conforming the :class:`ISQLQuote` + protocol (e.g. exposing a :meth:`getquoted` method). The :class:`AsIs` is + often useful for this task. + + Once an object is registered, it can be safely used in SQL queries and by + the :func:`adapt` function. + +.. class:: ISQLQuote + + Represents the SQL adaptation protocol. Objects conforming this protocol + should implement a :meth:`getquoted` method. + + .. todo:: has Psycopg user ever to explicitely use this object? + + .. todo:: + what the ISQLQuote methods are for? In my understanding the + class is only used as symbol to dispatch adaptation and not to be + instantiated. + +.. class:: AsIs + + Adapter conform to the :class:`ISQLQuote` protocol useful for objects + whose string representation is already valid as SQL representation. + + .. method:: getquoted() + + Return the ``str()`` conversion of the wrapped object. :: + + >>> AsIs(42).getquoted() + '42' + +.. class:: QuotedString + + Adapter conform to the :class:`ISQLQuote` protocol for string-like + objects. + + .. method:: getquoted() + + Return the string enclosed in single quotes. Any single quote + appearing in the the string is escaped by doubling it according to SQL + string constants syntax. Backslashes are escaped too. + + >>> QuotedString(r"O'Reilly").getquoted() + "'O''Reilly'" + +.. class:: Binary + + Adapter conform to the :class:`ISQLQuote` protocol for binary objects. + + .. method:: getquoted() + + Return the string enclosed in single quotes. It performs the same + escaping of the :class:`QuotedString` adapter, plus it knows how to + escape non-printable chars. + + >>> Binary("\x00\x08\x0F").getquoted() + "'\\\\000\\\\010\\\\017'" + + .. todo:: + + this class is actually not importd in module extensions: I'd say this + is a bug. + + +.. data:: Boolean +.. data:: Float + +Specialized adapters for builtin objects. + +.. data:: DateFromPy +.. data:: TimeFromPy +.. data:: TimestampFromPy +.. data:: IntervalFromPy + +Specialized adapters for Python datetime objects. + +.. data:: DateFromMx +.. data:: TimeFromMx +.. data:: TimestampFromMx +.. data:: IntervalFromMx + +Specialized adapters for `mx.DateTime`_ objects. + +.. data:: adapters + + Dictionary of the currently registered object adapters. Use + :func:`register_adapter` to add an adapter for a new type. + + + +Database types casting functions +-------------------------------- + +These functions are used to manipulate type casters to convert from PostgreSQL +types to Python objects. See :ref:`type-casting-from-sql-to-python` for +details. + +.. function:: new_type(oids, name, adapter) + + Create a new type caster to convert from a PostgreSQL type to a Python + object. The created object must be registered using + :func:`register_type` to be used. + + :param oids: tuple of OIDs of the PostgreSQL type to convert. + :param name: the name of the new type adapter. + :param adapter: the adaptation function. + + The object OID can be read from the :data:`cursor.description` or directly + from the PostgreSQL catalog. + + :data:`adapter` should have signature ``fun(value, cur)`` where + ``value`` is the string representation returned by PostgreSQL and ``cur`` + is the cursor from which data are read. In case of ``NULL``, ``value`` is + ``None``. The adapter should return the converted object. + + See :ref:`type-casting-from-sql-to-python` for an usage example. + +.. function:: register_type(obj [, scope]) + + Register a type caster created using :func:`new_type`. + + If :obj:`scope` is specified, it should be a :class:`connection` or a + :class:`cursor`: the type caster will be effective only limited to the + specified object. Otherwise it will be globally registered. + + .. todo:: Please confirm the above behaviour. + +.. data:: string_types + + The global register of type casters. + +.. data:: binary_types + + .. todo:: is this used? @@ -60,9 +220,9 @@ one of the following constants: No transaction is started when command are issued and no ``commit()`` or ``rollback()`` is required. Some PostgreSQL command such as ``CREATE DATABASE`` can't run into a transaction: to run such command use:: - + >>> conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) - + .. data:: ISOLATION_LEVEL_READ_UNCOMMITTED This isolation level is defined in the SQL standard but not available in @@ -75,13 +235,13 @@ one of the following constants: :meth:`cursor.execute()` command on a cursor and at each new ``execute()`` after a :meth:`connection.commit()` or a :meth:`connection.rollback()`. The transaction runs in the PostgreSQL ``READ COMMITTED`` isolation level. - + .. data:: ISOLATION_LEVEL_REPEATABLE_READ This isolation level is defined in the SQL standard but not available in the MVCC model of PostgreSQL: it is replaced by the stricter ``SERIALIZABLE``. - + .. data:: ISOLATION_LEVEL_SERIALIZABLE Transactions are run at a ``SERIALIZABLE`` isolation level. This is the diff --git a/doc/usage.rst b/doc/usage.rst index 9273248..3ec9d9a 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -23,7 +23,7 @@ basic commands:: # Pass data to fill a query placeholders and let Psycopg perform # the correct conversion (no more SQL injections!) - >>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", + >>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", ... (100, "abc'def")) # Query the database and obtain data as Python objects @@ -46,7 +46,7 @@ The main entry point of Psycopg are: - The class :class:`connection` encapsulates a database session. It allows to: - - terminate the session using the methods :meth:`connection.commit()` and + - terminate the session using the methods :meth:`connection.commit()` and :meth:`connection.rollback()`, - create new :class:`cursor`\ s to execute database commands and queries @@ -59,7 +59,7 @@ The main entry point of Psycopg are: - retrieve data using the methods :meth:`cursor.fetchone()`, :meth:`cursor.fetchmany()`, :meth:`cursor.fetchall()`. - + .. index:: Transaction, Begin, Commit, Rollback, Autocommit @@ -154,7 +154,7 @@ Passing parameters to SQL queries --------------------------------- Psycopg casts Python variables to SQL literals by type. `Standard Python types -are already adapted to the proper SQL literal`__. +are already adapted to the proper SQL literal`__. .. __: python-types-adaptation_ @@ -184,7 +184,7 @@ Notice that: - The Python string operator ``%`` is not used: the :meth:`cursor.execute()` method accepts a tuple or dictionary of values as second parameter. |sql-warn|__. - + .. |sql-warn| replace:: **Never** use ``%`` or ``+`` to merge values into queries @@ -218,7 +218,7 @@ Adaptation of Python values to SQL types ---------------------------------------- Many standards Python types are adapted into SQL and returned as Python -objects when a query is executed. +objects when a query is executed. If you need to convert other Python types to and from PostgreSQL data types, see :ref:`adapting-new-types` and :ref:`type-casting-from-sql-to-python`. @@ -301,12 +301,19 @@ the SQL string that would be sent to the database. >>> cur.mogrify("SELECT %s IN %s;", (10, (10, 20, 30))) 'SELECT 10 IN (10, 20, 30);' - .. note:: + .. note:: SQL doesn't allow an empty list in the IN operator, so your code should guard against empty tuples. + .. note:: + + In order to use the tuple adapter, your application must import the module + :mod:`psycopg2.extensions`. + .. todo:: is this a bug or a feature? + + .. versionadded:: 2.0.6 .. index:: pair: Server side; Cursor @@ -359,7 +366,7 @@ the same session (accessing to the same connection and creating separate -.. index:: +.. index:: pair: COPY; SQL command .. _copy: |