Changeset 784 for trunk/pgdb.py


Ignore:
Timestamp:
Jan 26, 2016, 12:17:23 PM (4 years ago)
Author:
cito
Message:

Make type cache and cursor description more useful

The type cache now stores some more information, e.g. whether a type is a base
type or a composite type and the category of the type. This may be later used
for casting composite types, or exposed to the user.

The cursor description now contains proper information on the size of numeric
types (including precision and scale).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/pgdb.py

    r781 r784  
    157157
    158158
     159TypeInfo = namedtuple('TypeInfo',
     160    ['oid', 'name', 'len', 'type', 'category', 'delim', 'relid'])
     161
     162
    159163class TypeCache(dict):
    160     """Cache for database types."""
     164    """Cache for database types.
     165
     166    This cache maps type OIDs to TypeInfo tuples containing the name
     167    and other important info for the database type with the given OID.
     168    """
    161169
    162170    def __init__(self, cnx):
    163171        """Initialize type cache for connection."""
    164172        super(TypeCache, self).__init__()
     173        self._escape_string = cnx.escape_string
    165174        self._src = cnx.source()
     175
     176    def __missing__(self, key):
     177        q = ("SELECT oid, typname,"
     178             " typlen, typtype, typcategory, typdelim, typrelid"
     179            " FROM pg_type WHERE ")
     180        if isinstance(key, int):
     181            q += "oid = %d" % key
     182        else:
     183            q += "typname = '%s'" % self._escape_string(key)
     184        self._src.execute(q)
     185        res = list(self._src.fetch(1)[0])
     186        res[0] = int(res[0])
     187        res[2] = int(res[2])
     188        res[6] = int(res[6])
     189        res = TypeInfo(*res)
     190        self[res.oid] = self[res.name] = res
     191        return res
    166192
    167193    @staticmethod
     
    182208            return cast(value)
    183209
    184     def getdescr(self, oid):
    185         """Get name of database type with given oid."""
    186         try:
    187             return self[oid]
    188         except KeyError:
    189             self._src.execute(
    190                 "SELECT typname, typlen "
    191                 "FROM pg_type WHERE oid=%s" % oid)
    192             res = self._src.fetch(1)[0]
    193             # The column name is omitted from the return value.
    194             # It will have to be prepended by the caller.
    195             res = (res[0], None, int(res[1]), None, None, None)
    196             self[oid] = res
    197             return res
    198 
    199210
    200211_re_array_escape = regex(r'(["\\])')
    201212_re_array_quote = regex(r'[{},"\\\s]|^[Nn][Uu][Ll][Ll]$')
     213
    202214
    203215class _quotedict(dict):
     
    315327            parameters = tuple(map(self._quote, parameters))
    316328        return string % parameters
     329
     330    def _make_description(self, info):
     331        """Make the description tuple for the given field info."""
     332        name, typ, size, mod = info[1:]
     333        type_info = self._type_cache[typ]
     334        type_code = type_info.name
     335        if mod > 0:
     336            mod -= 4
     337        if type_code == 'numeric':
     338            precision, scale = mod >> 16, mod & 0xffff
     339            size = precision
     340        else:
     341            if not size:
     342                size = type_info.size
     343            if size == -1:
     344                size = mod
     345            precision = scale = None
     346        return CursorDescription(name, type_code,
     347            None, size, precision, scale, None)
    317348
    318349    def close(self):
     
    378409        if self._src.resulttype == RESULT_DQL:
    379410            self.rowcount = self._src.ntuples
    380             getdescr = self._type_cache.getdescr
    381             description = [CursorDescription(
    382                 info[1], *getdescr(info[2])) for info in self._src.listinfo()]
    383             self.colnames = [info[0] for info in description]
    384             self.coltypes = [info[1] for info in description]
     411            description = self._make_description
     412            description = [description(info) for info in self._src.listinfo()]
     413            self.colnames = [d[0] for d in description]
     414            self.coltypes = [d[1] for d in description]
    385415            self.description = description
    386416            self.lastrowid = None
Note: See TracChangeset for help on using the changeset viewer.