From eb6d152902aa7ec58d5bcd34ffdb6c0ec7cdb0ea Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Sun, 26 Sep 2021 22:18:54 +0200 Subject: [PATCH] Improve AdaptersMap and Copy.set_types() docs --- docs/api/adapt.rst | 1 + docs/basic/copy.rst | 5 ++--- psycopg/psycopg/_adapters_map.py | 22 +++++++++++++++++++--- psycopg/psycopg/copy.py | 17 +++++++++++++---- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/docs/api/adapt.rst b/docs/api/adapt.rst index 3d7b34843..36bafa97e 100644 --- a/docs/api/adapt.rst +++ b/docs/api/adapt.rst @@ -67,6 +67,7 @@ Other objects used in adaptations :type: `~psycopg.types.TypesRegistry` .. automethod:: get_dumper + .. automethod:: get_dumper_by_oid .. automethod:: get_loader diff --git a/docs/basic/copy.rst b/docs/basic/copy.rst index 89af1a6f7..2761b3dbf 100644 --- a/docs/basic/copy.rst +++ b/docs/basic/copy.rst @@ -79,9 +79,8 @@ must have a binary dumper registered (see see :ref:`binary-data`). Note that PostgreSQL is particularly finicky when loading data in binary mode and will apply *no cast rule*. This means that e.g. passing the value 100 to an `integer` column will fail because Psycopg will pass it as a `smallint` -value. You can work around the problem by registering the right binary -`~adapt.Dumper` on the cursor (see :ref:`adaptation`) or using the right data -wrapper (e.g. `~psycopg.types.numeric.Int4`). +value. You can work around the problem using the `~Copy.set_types()` method of +the `!Copy` object and specify carefully the types to dump. .. _copy-out-row: diff --git a/psycopg/psycopg/_adapters_map.py b/psycopg/psycopg/_adapters_map.py index a2a9681e4..4fe93c282 100644 --- a/psycopg/psycopg/_adapters_map.py +++ b/psycopg/psycopg/_adapters_map.py @@ -125,6 +125,12 @@ class AdaptersMap: will be possible to register it without importing it before. In this case it should be the fully qualified name of the object (e.g. ``"uuid.UUID"``). + + If *cls* is None, only use the dumper when looking up using + `get_dumper_by_oid()`, which happens when we know the Postgres type to + adapt to, but not the Python type that will be adapted (e.g. in COPY + after using `~psycopg.Copy.set_types()`). + """ if not (cls is None or isinstance(cls, (str, type))): raise TypeError( @@ -188,7 +194,11 @@ class AdaptersMap: """ Return the dumper class for the given type and format. - Raise ProgrammingError if a class is not available. + Raise `~psycopg.ProgrammingError` if a class is not available. + + :param cls: The class to adapt. + :param format: The format to dump to. If `~psycopg.adapt.PyFormat.AUTO`, + use the last one of the dumpers registered on *cls*. """ try: dmap = self._dumpers[format] @@ -216,7 +226,10 @@ class AdaptersMap: """ Return the dumper class for the given oid and format. - Raise ProgrammingError if a class is not available. + Raise `~psycopg.ProgrammingError` if a class is not available. + + :param oid: The oid of the type to dump to. + :param format: The format to dump to. """ try: dmap = self._dumpers_by_oid[format] @@ -245,7 +258,10 @@ class AdaptersMap: """ Return the loader class for the given oid and format. - Return None if not found. + Return `!None` if not found. + + :param oid: The oid of the type to load. + :param format: The format to load from. """ return self._loaders[format].get(oid) diff --git a/psycopg/psycopg/copy.py b/psycopg/psycopg/copy.py index a237078f5..b26041a08 100644 --- a/psycopg/psycopg/copy.py +++ b/psycopg/psycopg/copy.py @@ -84,13 +84,22 @@ class BaseCopy(Generic[ConnectionType]): def set_types(self, types: Sequence[Union[int, str]]) -> None: """ - Set the types expected out of a :sql:`COPY TO` operation. - - Without setting the types, the data from :sql:`COPY TO` will be - returned as unparsed strings or bytes. + Set the types expected in a COPY operation. The types must be specified as a sequence of oid or PostgreSQL type names (e.g. ``int4``, ``timestamptz[]``). + + This operation overcomes the lack of metadata returned by PostgreSQL + when a COPY operation begins: + + - On :sql:`COPY TO`, `!set_types()` allows to specify what types the + operation returns. If `!set_types()` is not used, the data will be + reurned as unparsed strings or bytes instead of Python objects. + + - On :sql:`COPY FROM`, `!set_types()` allows to choose what type the + database expects. This is especially useful in binary copy, because + PostgreSQL will apply no cast rule. + """ registry = self.cursor.adapters.types oids = [ -- 2.47.3