Skip to content

Commit

Permalink
Add typlen attribute to the DBTypes class
Browse files Browse the repository at this point in the history
  • Loading branch information
Cito committed Jun 20, 2020
1 parent f82d4cd commit 584ca74
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 17 deletions.
2 changes: 2 additions & 0 deletions docs/contents/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Version 5.2 (to be released)
instead of type names. Suggested by Justin Pryzby (#38).
- The `inserttable()` method now accepts an optional column list that will
be passed on to the COPY command. Contributed by Justin Pryzby (#24).
- The `DBTyptes` class now also includes the `typlen` attribute with
information about the size of the type (contributed by Justin Pryzby).

- Changes to the DB-API 2 module (pgdb):
- When using Python 2, errors are now derived from StandardError
Expand Down
2 changes: 1 addition & 1 deletion docs/contents/pg/connection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ It inserts the whole values list into the given table. Internally, it
uses the COPY command of the PostgreSQL database. The list is a list
of tuples/lists that define the values for each inserted row. The rows
values may contain string, integer, long or double (real) values.
``columns`` is a optional sequence of column names to be passed on
``columns`` is an optional sequence of column names to be passed on
to the COPY command.

.. warning::
Expand Down
7 changes: 4 additions & 3 deletions docs/contents/pg/db_types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ returned by :meth:`DB.get_attnames` as dictionary values).

These type names are strings which are equal to either the simple PyGreSQL
names or to the more fine-grained registered PostgreSQL type names if these
have been enabled with :meth:`DB.use_regtypes`. Besides being strings, they
carry additional information about the associated PostgreSQL type in the
following attributes:
have been enabled with :meth:`DB.use_regtypes`. Type names are strings that
are augmented with additional information about the associated PostgreSQL
type that can be inspected using the following attributes:

- *oid* -- the PostgreSQL type OID
- *pgtype* -- the internal PostgreSQL data type name
- *regtype* -- the registered PostgreSQL data type name
- *simple* -- the more coarse-grained PyGreSQL type name
- *typlen* -- internal size of the type, negative if variable
- *typtype* -- `b` = base type, `c` = composite type etc.
- *category* -- `A` = Array, `b` =Boolean, `C` = Composite etc.
- *delim* -- delimiter for array types
Expand Down
29 changes: 16 additions & 13 deletions pg.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ def get_type_dict(self):
def _quote_if_unqualified(param, name):
"""Quote parameter representing a qualified name.
Puts a quote_ident() call around the give parameter unless
Puts a quote_ident() call around the given parameter unless
the name contains a dot, in which case the name is ambiguous
(could be a qualified name or just a name with a dot in it)
and must be quoted manually by the caller.
Expand Down Expand Up @@ -1212,6 +1212,7 @@ class DbType(str):
pgtype: the internal PostgreSQL data type name
regtype: the registered PostgreSQL data type name
simple: the more coarse-grained PyGreSQL type name
typlen: the internal size, negative if variable
typtype: b = base type, c = composite type etc.
category: A = Array, b = Boolean, C = Composite etc.
delim: delimiter for array types
Expand Down Expand Up @@ -1245,21 +1246,21 @@ def __init__(self, db):
self._typecasts.get_attnames = self.get_attnames
self._typecasts.connection = self._db
if db.server_version < 80400:
# older remote databases (not officially supported)
# very old remote databases (not officially supported)
self._query_pg_type = (
"SELECT oid, typname, typname::text::regtype,"
" typtype, null as typcategory, typdelim, typrelid"
" typlen, typtype, null as typcategory, typdelim, typrelid"
" FROM pg_catalog.pg_type"
" WHERE oid OPERATOR(pg_catalog.=) %s::regtype")
else:
self._query_pg_type = (
"SELECT oid, typname, typname::regtype,"
" typtype, typcategory, typdelim, typrelid"
" typlen, typtype, typcategory, typdelim, typrelid"
" FROM pg_catalog.pg_type"
" WHERE oid OPERATOR(pg_catalog.=) %s::regtype")

def add(self, oid, pgtype, regtype,
typtype, category, delim, relid):
typlen, typtype, category, delim, relid):
"""Create a PostgreSQL type name with additional info."""
if oid in self:
return self[oid]
Expand All @@ -1269,6 +1270,7 @@ def add(self, oid, pgtype, regtype,
typ.simple = simple
typ.pgtype = pgtype
typ.regtype = regtype
typ.typlen = typlen
typ.typtype = typtype
typ.category = category
typ.delim = delim
Expand All @@ -1284,7 +1286,7 @@ def __missing__(self, key):
except ProgrammingError:
res = None
if not res:
raise KeyError('Type %s could not be found' % key)
raise KeyError('Type %s could not be found' % (key,))
res = res[0]
typ = self.add(*res)
self[typ.oid] = self[typ.pgtype] = typ
Expand Down Expand Up @@ -1610,24 +1612,25 @@ def __init__(self, *args, **kw):
self.adapter = Adapter(self)
self.dbtypes = DbTypes(self)
if db.server_version < 80400:
# support older remote data bases (not officially supported)
# very old remote databases (not officially supported)
self._query_attnames = (
"SELECT a.attname, t.oid, t.typname, t.typname::text::regtype,"
" t.typtype, null as typcategory, t.typdelim, t.typrelid"
" t.typlen, t.typtype, null as typcategory,"
" t.typdelim, t.typrelid"
" FROM pg_catalog.pg_attribute a"
" JOIN pg_catalog.pg_type t"
" ON t.oid OPERATOR(pg_catalog.=) a.atttypid"
" WHERE a.attrelid OPERATOR(pg_catalog.=) %s::regclass AND %s"
" AND NOT a.attisdropped ORDER BY a.attnum")
" WHERE a.attrelid OPERATOR(pg_catalog.=) %s::regclass"
" AND %s AND NOT a.attisdropped ORDER BY a.attnum")
else:
self._query_attnames = (
"SELECT a.attname, t.oid, t.typname, t.typname::regtype,"
" t.typtype, t.typcategory, t.typdelim, t.typrelid"
" t.typlen, t.typtype, t.typcategory, t.typdelim, t.typrelid"
" FROM pg_catalog.pg_attribute a"
" JOIN pg_catalog.pg_type t"
" ON t.oid OPERATOR(pg_catalog.=) a.atttypid"
" WHERE a.attrelid OPERATOR(pg_catalog.=) %s::regclass AND %s"
" AND NOT a.attisdropped ORDER BY a.attnum")
" WHERE a.attrelid OPERATOR(pg_catalog.=) %s::regclass"
" AND %s AND NOT a.attisdropped ORDER BY a.attnum")
db.set_cast_hook(self.dbtypes.typecast)
# For debugging scripts, self.debug can be set
# * to a string format specification (e.g. in CGI set to "%s<BR>"),
Expand Down
4 changes: 4 additions & 0 deletions tests/test_classic_dbwrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -4062,6 +4062,7 @@ def testDbTypesInfo(self):
self.assertEqual(typ.pgtype, 'numeric')
self.assertEqual(typ.regtype, 'numeric')
self.assertEqual(typ.simple, 'num')
self.assertEqual(typ.typlen, -1)
self.assertEqual(typ.typtype, 'b')
self.assertEqual(typ.category, 'N')
self.assertEqual(typ.delim, ',')
Expand All @@ -4075,6 +4076,7 @@ def testDbTypesInfo(self):
self.assertEqual(typ.pgtype, 'pg_type')
self.assertEqual(typ.regtype, 'pg_type')
self.assertEqual(typ.simple, 'record')
self.assertEqual(typ.typlen, -1)
self.assertEqual(typ.typtype, 'c')
self.assertEqual(typ.category, 'C')
self.assertEqual(typ.delim, ',')
Expand All @@ -4086,11 +4088,13 @@ def testDbTypesInfo(self):
self.assertIn('typname', attnames)
typname = attnames['typname']
self.assertEqual(typname, 'name' if self.regtypes else 'text')
self.assertEqual(typname.typlen, 64) # base
self.assertEqual(typname.typtype, 'b') # base
self.assertEqual(typname.category, 'S') # string
self.assertIn('typlen', attnames)
typlen = attnames['typlen']
self.assertEqual(typlen, 'smallint' if self.regtypes else 'int')
self.assertEqual(typlen.typlen, 2) # base
self.assertEqual(typlen.typtype, 'b') # base
self.assertEqual(typlen.category, 'N') # numeric

Expand Down

0 comments on commit 584ca74

Please sign in to comment.