Changeset 796


Ignore:
Timestamp:
Jan 28, 2016, 4:25:36 PM (4 years ago)
Author:
cito
Message:

Enrich type_code strings in the DB API 2

The type codes now carry e.g. the information whether a type is a record.
This allows to provide a RECORD type object that compares equal to all
kinds of records, similar to the already existing ARRAY type object.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/docs/contents/changelog.rst

    r795 r796  
    2727    The column names and types can now also be requested through the
    2828    colnames and coltypes attributes, which are not part of DB-API 2 though.
     29    The type_code provided by the description attribute is still equal to
     30    the PostgreSQL internal type name, but now carries some more information
     31    in additional attributes. The size, precision and scale information that
     32    is part of the description is now properly set for numeric types.
    2933  - If you pass a Python list as one of the parameters to a DB-API 2 cursor,
    3034    it is now automatically bound using an ARRAY constructor. If you pass a
  • trunk/docs/contents/pgdb/connection.rst

    r789 r796  
    8282.. attribute:: Connection.type_cache
    8383
    84     A dictionary with type information on the PostgreSQL types
     84    A dictionary with the various type codes for the PostgreSQL types
    8585
    86 You can request the dictionary either via type names or type OIDs.
     86You can request the dictionary either via a PostgreSQL type name (which
     87(is equal to the DB-API 2 *type_code*) or via a PostgreSQL type OIDs.
    8788
    88 The values are named tuples containing the following fields:
     89The values are *type_code* strings carrying additional attributes:
    8990
    9091        - *oid* -- the OID of the type
    91         - *name*  -- the type's name
    9292        - *len*  -- the internal size
    9393        - *type*  -- ``'b'`` = base, ``'c'`` = composite, ...
  • trunk/docs/contents/pgdb/types.rst

    r775 r796  
    157157    Used to describe ``json`` and ``jsonb`` columns
    158158
     159.. object:: ARRAY
     160
     161    Used to describe columns containing PostgreSQL arrays
     162
     163.. object:: RECORD
     164
     165    Used to describe columns containing PostgreSQL records
     166
    159167Example for using some type objects::
    160168
  • trunk/pgdb.py

    r792 r796  
    159159
    160160
    161 TypeInfo = namedtuple('TypeInfo',
    162     ['oid', 'name', 'len', 'type', 'category', 'delim', 'relid'])
     161class TypeCode(str):
     162    """Class representing the type_code used by the DB-API 2.0.
     163
     164    TypeCode objects are strings equal to the PostgreSQL type name,
     165    but carry some additional information.
     166    """
     167
     168    @classmethod
     169    def create(cls, oid, name, len, type, category, delim, relid):
     170        """Create a type code for a PostgreSQL data type."""
     171        self = cls(name)
     172        self.oid = oid
     173        self.len = len
     174        self.type = type
     175        self.category = category
     176        self.delim = delim
     177        self.relid = relid
     178        return self
    163179
    164180ColumnInfo = namedtuple('ColumnInfo', ['name', 'type'])
     
    168184    """Cache for database types.
    169185
    170     This cache maps type OIDs and names to TypeInfo tuples containing
     186    This cache maps type OIDs and names to TypeCode strings containing
    171187    important information on the associated database type.
    172188    """
     
    196212        if not res:
    197213            raise KeyError('Type %s could not be found' % key)
    198         res = list(res[0])
    199         res[0] = int(res[0])
    200         res[2] = int(res[2])
    201         res[6] = int(res[6])
    202         res = TypeInfo(*res)
    203         self[res.oid] = self[res.name] = res
    204         return res
     214        res = res[0]
     215        type_code = TypeCode.create(int(res[0]), res[1],
     216            int(res[2]), res[3], res[4], res[5], int(res[6]))
     217        self[type_code.oid] = self[str(type_code)] = type_code
     218        return type_code
    205219
    206220    def get(self, key, default=None):
     
    255269        if isinstance(key, int):
    256270            try:
    257                 typ = self[key].name
     271                typ = self[key]
    258272            except KeyError:
    259273                return None
     
    368382        """Make the description tuple for the given field info."""
    369383        name, typ, size, mod = info[1:]
    370         type_info = self.type_cache[typ]
    371         type_code = type_info.name
     384        type_code = self.type_cache[typ]
    372385        if mod > 0:
    373386            mod -= 4
     
    10431056
    10441057
     1058class RecordType:
     1059    """Type class for PostgreSQL record types."""
     1060
     1061    def __eq__(self, other):
     1062        if isinstance(other, TypeCode):
     1063            return other.type == 'c'
     1064        elif isinstance(other, basestring):
     1065            return other == 'record'
     1066        else:
     1067            return isinstance(other, RecordType)
     1068
     1069    def __ne__(self, other):
     1070        if isinstance(other, TypeCode):
     1071            return other.type != 'c'
     1072        elif isinstance(other, basestring):
     1073            return other != 'record'
     1074        else:
     1075            return not isinstance(other, RecordType)
     1076
     1077
    10451078# Mandatory type objects defined by DB-API 2 specs:
    10461079
     
    10721105ARRAY = ArrayType()
    10731106
     1107# Type object for records (encompassing all composite types):
     1108
     1109RECORD = RecordType()
     1110
    10741111
    10751112# Mandatory type helpers defined by DB-API 2 specs:
  • trunk/tests/test_dbapi20.py

    r792 r796  
    298298        type_info = type_cache['numeric']
    299299        self.assertIn('numeric', type_cache)
     300        self.assertEqual(type_info, 'numeric')
    300301        self.assertEqual(type_info.oid, 1700)
    301         self.assertEqual(type_info.name, 'numeric')
    302302        self.assertEqual(type_info.type, 'b')  # base
    303303        self.assertEqual(type_info.category, 'N')  # numeric
     
    312312        self.assertEqual(cols[0].name, 'typname')
    313313        typname = type_cache[cols[0].type]
    314         self.assertEqual(typname.name, 'name')
     314        self.assertEqual(typname, 'name')
    315315        self.assertEqual(typname.type, 'b')  # base
    316316        self.assertEqual(typname.category, 'S')  # string
    317317        self.assertEqual(cols[3].name, 'typlen')
    318318        typlen = type_cache[cols[3].type]
    319         self.assertEqual(typlen.name, 'int2')
     319        self.assertEqual(typlen, 'int2')
    320320        self.assertEqual(typlen.type, 'b')  # base
    321321        self.assertEqual(typlen.category, 'N')  # numeric
     
    425425            self.assertEqual(cur.description[0].type_code, pgdb.FLOAT)
    426426            self.assertNotEqual(cur.description[0].type_code, pgdb.ARRAY)
     427            self.assertNotEqual(cur.description[0].type_code, pgdb.RECORD)
    427428        finally:
    428429            con.close()
     
    462463            self.assertEqual(cur.description[0].type_code, pgdb.DATETIME)
    463464            self.assertNotEqual(cur.description[0].type_code, pgdb.ARRAY)
     465            self.assertNotEqual(cur.description[0].type_code, pgdb.RECORD)
    464466        finally:
    465467            con.close()
     
    489491                " (%%d,%%s::int[],%%s::text[][])" % table, params)
    490492            cur.execute("select i, t from %s order by n" % table)
    491             self.assertEqual(cur.description[0].type_code, pgdb.ARRAY)
    492             self.assertEqual(cur.description[0].type_code, pgdb.NUMBER)
    493             self.assertEqual(cur.description[0].type_code, pgdb.INTEGER)
    494             self.assertEqual(cur.description[1].type_code, pgdb.ARRAY)
    495             self.assertEqual(cur.description[1].type_code, pgdb.STRING)
     493            d = cur.description
     494            self.assertEqual(d[0].type_code, pgdb.ARRAY)
     495            self.assertNotEqual(d[0].type_code, pgdb.RECORD)
     496            self.assertEqual(d[0].type_code, pgdb.NUMBER)
     497            self.assertEqual(d[0].type_code, pgdb.INTEGER)
     498            self.assertEqual(d[1].type_code, pgdb.ARRAY)
     499            self.assertNotEqual(d[1].type_code, pgdb.RECORD)
     500            self.assertEqual(d[1].type_code, pgdb.STRING)
    496501            rows = cur.fetchall()
    497502        finally:
     
    526531            type_code = cur.description[0].type_code
    527532            self.assertEqual(type_code, record)
     533            self.assertEqual(type_code, pgdb.RECORD)
     534            self.assertNotEqual(type_code, pgdb.ARRAY)
    528535            columns = con.type_cache.columns(type_code)
    529536            self.assertEqual(columns[0].name, 'name')
    530537            self.assertEqual(columns[1].name, 'age')
    531             self.assertEqual(con.type_cache[columns[0].type].name, 'varchar')
    532             self.assertEqual(con.type_cache[columns[1].type].name, 'int4')
     538            self.assertEqual(con.type_cache[columns[0].type], 'varchar')
     539            self.assertEqual(con.type_cache[columns[1].type], 'int4')
    533540            rows = cur.fetchall()
    534541        finally:
     
    879886        self.assertEqual('_char', pgdb.ARRAY)
    880887        self.assertNotEqual('char', pgdb.ARRAY)
     888        self.assertEqual(pgdb.RECORD, pgdb.RECORD)
     889        self.assertNotEqual(pgdb.RECORD, pgdb.STRING)
     890        self.assertNotEqual(pgdb.RECORD, pgdb.ARRAY)
     891        self.assertEqual('record', pgdb.RECORD)
     892        self.assertNotEqual('_record', pgdb.RECORD)
    881893
    882894
Note: See TracChangeset for help on using the changeset viewer.