source: trunk/pg.py @ 770

Last change on this file since 770 was 770, checked in by cito, 4 years ago

Add methods for getting a table as a list or dict

Also added documentation and 100% test coverage.

The get_attnames() method now always returns a read-only ordered dictionary,
even under Python 2.6 or 3.0. So you can sure the columns will be returned
in the right order if you iterate over it, and that you don't accidentally
modify the dictionary (since it is cached).

  • Property svn:keywords set to Id
File size: 56.2 KB
Line 
1#! /usr/bin/python
2#
3# pg.py
4#
5# $Id: pg.py 770 2016-01-20 18:19:45Z cito $
6#
7
8"""PyGreSQL classic interface.
9
10This pg module implements some basic database management stuff.
11It includes the _pg module and builds on it, providing the higher
12level wrapper class named DB with additional functionality.
13This is known as the "classic" ("old style") PyGreSQL interface.
14For a DB-API 2 compliant interface use the newer pgdb module.
15"""
16
17# Copyright (c) 1997-2016 by D'Arcy J.M. Cain.
18#
19# Contributions made by Ch. Zwerschke and others.
20#
21# The notification handler is based on pgnotify which is
22# Copyright (c) 2001 Ng Pheng Siong. All rights reserved.
23#
24# Permission to use, copy, modify, and distribute this software and its
25# documentation for any purpose and without fee is hereby granted,
26# provided that the above copyright notice appear in all copies and that
27# both that copyright notice and this permission notice appear in
28# supporting documentation.
29
30from __future__ import print_function
31
32from _pg import *
33
34import select
35import warnings
36
37from decimal import Decimal
38from collections import namedtuple
39from functools import partial
40from operator import itemgetter
41
42try:
43    basestring
44except NameError:  # Python >= 3.0
45    basestring = (str, bytes)
46
47set_decimal(Decimal)
48
49try:
50    from collections import OrderedDict
51except ImportError:  # Python 2.6 or 3.0
52    OrderedDict = dict
53
54
55    class AttrDict(dict):
56        """Simple read-only ordered dictionary for storing attribute names."""
57
58        def __init__(self, *args, **kw):
59            if len(args) > 1 or kw:
60                raise TypeError
61            items = args[0] if args else []
62            if isinstance(items, dict):
63                raise TypeError
64            items = list(items)
65            self._keys = [item[0] for item in items]
66            dict.__init__(self, items)
67            self._read_only = True
68            error = self._read_only_error
69            self.clear = self.update = error
70            self.pop = self.setdefault = self.popitem = error
71
72        def __setitem__(self, key, value):
73            if self._read_only:
74                self._read_only_error()
75            dict.__setitem__(self, key, value)
76
77        def __delitem__(self, key):
78            if self._read_only:
79                self._read_only_error()
80            dict.__delitem__(self, key)
81
82        def __iter__(self):
83            return iter(self._keys)
84
85        def keys(self):
86            return list(self._keys)
87
88        def values(self):
89            return [self[key] for key in self]
90
91        def items(self):
92            return [(key, self[key]) for key in self]
93
94        def iterkeys(self):
95            return self.__iter__()
96
97        def itervalues(self):
98            return iter(self.values())
99
100        def iteritems(self):
101            return iter(self.items())
102
103        @staticmethod
104        def _read_only_error(*args, **kw):
105            raise TypeError('This object is read-only')
106
107else:
108
109     class AttrDict(OrderedDict):
110        """Simple read-only ordered dictionary for storing attribute names."""
111
112        def __init__(self, *args, **kw):
113            self._read_only = False
114            OrderedDict.__init__(self, *args, **kw)
115            self._read_only = True
116            error = self._read_only_error
117            self.clear = self.update = error
118            self.pop = self.setdefault = self.popitem = error
119
120        def __setitem__(self, key, value):
121            if self._read_only:
122                self._read_only_error()
123            OrderedDict.__setitem__(self, key, value)
124
125        def __delitem__(self, key):
126            if self._read_only:
127                self._read_only_error()
128            OrderedDict.__delitem__(self, key)
129
130        @staticmethod
131        def _read_only_error(*args, **kw):
132            raise TypeError('This object is read-only')
133
134
135
136# Auxiliary functions that are independent from a DB connection:
137
138def _oid_key(table):
139    """Build oid key from a table name."""
140    return 'oid(%s)' % table
141
142
143def _simpletype(typ):
144    """Determine a simplified name a pg_type name."""
145    if typ.startswith('bool'):
146        return 'bool'
147    if typ.startswith(('abstime', 'date', 'interval', 'timestamp')):
148        return 'date'
149    if typ.startswith(('cid', 'oid', 'int', 'xid')):
150        return 'int'
151    if typ.startswith('float'):
152        return 'float'
153    if typ.startswith('numeric'):
154        return 'num'
155    if typ.startswith('money'):
156        return 'money'
157    if typ.startswith('bytea'):
158        return 'bytea'
159    return 'text'
160
161
162def _namedresult(q):
163    """Get query result as named tuples."""
164    row = namedtuple('Row', q.listfields())
165    return [row(*r) for r in q.getresult()]
166
167set_namedresult(_namedresult)
168
169
170class _MemoryQuery:
171    """Class that embodies a given query result."""
172
173    def __init__(self, result, fields):
174        """Create query from given result rows and field names."""
175        self.result = result
176        self.fields = fields
177
178    def listfields(self):
179        """Return the stored field names of this query."""
180        return self.fields
181
182    def getresult(self):
183        """Return the stored result of this query."""
184        return self.result
185
186
187def _db_error(msg, cls=DatabaseError):
188    """Return DatabaseError with empty sqlstate attribute."""
189    error = cls(msg)
190    error.sqlstate = None
191    return error
192
193
194def _int_error(msg):
195    """Return InternalError."""
196    return _db_error(msg, InternalError)
197
198
199def _prg_error(msg):
200    """Return ProgrammingError."""
201    return _db_error(msg, ProgrammingError)
202
203
204class NotificationHandler(object):
205    """A PostgreSQL client-side asynchronous notification handler."""
206
207    def __init__(self, db, event, callback=None,
208            arg_dict=None, timeout=None, stop_event=None):
209        """Initialize the notification handler.
210
211        You must pass a PyGreSQL database connection, the name of an
212        event (notification channel) to listen for and a callback function.
213
214        You can also specify a dictionary arg_dict that will be passed as
215        the single argument to the callback function, and a timeout value
216        in seconds (a floating point number denotes fractions of seconds).
217        If it is absent or None, the callers will never time out.  If the
218        timeout is reached, the callback function will be called with a
219        single argument that is None.  If you set the timeout to zero,
220        the handler will poll notifications synchronously and return.
221
222        You can specify the name of the event that will be used to signal
223        the handler to stop listening as stop_event. By default, it will
224        be the event name prefixed with 'stop_'.
225        """
226        self.db = db
227        self.event = event
228        self.stop_event = stop_event or 'stop_%s' % event
229        self.listening = False
230        self.callback = callback
231        if arg_dict is None:
232            arg_dict = {}
233        self.arg_dict = arg_dict
234        self.timeout = timeout
235
236    def __del__(self):
237        self.unlisten()
238
239    def close(self):
240        """Stop listening and close the connection."""
241        if self.db:
242            self.unlisten()
243            self.db.close()
244            self.db = None
245
246    def listen(self):
247        """Start listening for the event and the stop event."""
248        if not self.listening:
249            self.db.query('listen "%s"' % self.event)
250            self.db.query('listen "%s"' % self.stop_event)
251            self.listening = True
252
253    def unlisten(self):
254        """Stop listening for the event and the stop event."""
255        if self.listening:
256            self.db.query('unlisten "%s"' % self.event)
257            self.db.query('unlisten "%s"' % self.stop_event)
258            self.listening = False
259
260    def notify(self, db=None, stop=False, payload=None):
261        """Generate a notification.
262
263        Optionally, you can pass a payload with the notification.
264
265        If you set the stop flag, a stop notification will be sent that
266        will cause the handler to stop listening.
267
268        Note: If the notification handler is running in another thread, you
269        must pass a different database connection since PyGreSQL database
270        connections are not thread-safe.
271        """
272        if self.listening:
273            if not db:
274                db = self.db
275            q = 'notify "%s"' % (self.stop_event if stop else self.event)
276            if payload:
277                q += ", '%s'" % payload
278            return db.query(q)
279
280    def __call__(self):
281        """Invoke the notification handler.
282
283        The handler is a loop that listens for notifications on the event
284        and stop event channels.  When either of these notifications are
285        received, its associated 'pid', 'event' and 'extra' (the payload
286        passed with the notification) are inserted into its arg_dict
287        dictionary and the callback is invoked with this dictionary as
288        a single argument.  When the handler receives a stop event, it
289        stops listening to both events and return.
290
291        In the special case that the timeout of the handler has been set
292        to zero, the handler will poll all events synchronously and return.
293        If will keep listening until it receives a stop event.
294
295        Note: If you run this loop in another thread, don't use the same
296        database connection for database operations in the main thread.
297        """
298        self.listen()
299        poll = self.timeout == 0
300        if not poll:
301            rlist = [self.db.fileno()]
302        while self.listening:
303            if poll or select.select(rlist, [], [], self.timeout)[0]:
304                while self.listening:
305                    notice = self.db.getnotify()
306                    if not notice:  # no more messages
307                        break
308                    event, pid, extra = notice
309                    if event not in (self.event, self.stop_event):
310                        self.unlisten()
311                        raise _db_error(
312                            'Listening for "%s" and "%s", but notified of "%s"'
313                            % (self.event, self.stop_event, event))
314                    if event == self.stop_event:
315                        self.unlisten()
316                    self.arg_dict.update(pid=pid, event=event, extra=extra)
317                    self.callback(self.arg_dict)
318                if poll:
319                    break
320            else:   # we timed out
321                self.unlisten()
322                self.callback(None)
323
324
325def pgnotify(*args, **kw):
326    """Same as NotificationHandler, under the traditional name."""
327    warnings.warn("pgnotify is deprecated, use NotificationHandler instead",
328        DeprecationWarning, stacklevel=2)
329    return NotificationHandler(*args, **kw)
330
331
332# The actual PostGreSQL database connection interface:
333
334class DB(object):
335    """Wrapper class for the _pg connection type."""
336
337    def __init__(self, *args, **kw):
338        """Create a new connection
339
340        You can pass either the connection parameters or an existing
341        _pg or pgdb connection. This allows you to use the methods
342        of the classic pg interface with a DB-API 2 pgdb connection.
343        """
344        if not args and len(kw) == 1:
345            db = kw.get('db')
346        elif not kw and len(args) == 1:
347            db = args[0]
348        else:
349            db = None
350        if db:
351            if isinstance(db, DB):
352                db = db.db
353            else:
354                try:
355                    db = db._cnx
356                except AttributeError:
357                    pass
358        if not db or not hasattr(db, 'db') or not hasattr(db, 'query'):
359            db = connect(*args, **kw)
360            self._closeable = True
361        else:
362            self._closeable = False
363        self.db = db
364        self.dbname = db.db
365        self._regtypes = False
366        self._attnames = {}
367        self._pkeys = {}
368        self._privileges = {}
369        self._args = args, kw
370        self.debug = None  # For debugging scripts, this can be set
371            # * to a string format specification (e.g. in CGI set to "%s<BR>"),
372            # * to a file object to write debug statements or
373            # * to a callable object which takes a string argument
374            # * to any other true value to just print debug statements
375
376    def __getattr__(self, name):
377        # All undefined members are same as in underlying connection:
378        if self.db:
379            return getattr(self.db, name)
380        else:
381            raise _int_error('Connection is not valid')
382
383    def __dir__(self):
384        # Custom dir function including the attributes of the connection:
385        attrs = set(self.__class__.__dict__)
386        attrs.update(self.__dict__)
387        attrs.update(dir(self.db))
388        return sorted(attrs)
389
390    # Context manager methods
391
392    def __enter__(self):
393        """Enter the runtime context. This will start a transactio."""
394        self.begin()
395        return self
396
397    def __exit__(self, et, ev, tb):
398        """Exit the runtime context. This will end the transaction."""
399        if et is None and ev is None and tb is None:
400            self.commit()
401        else:
402            self.rollback()
403
404    # Auxiliary methods
405
406    def _do_debug(self, *args):
407        """Print a debug message"""
408        if self.debug:
409            s = '\n'.join(args)
410            if isinstance(self.debug, basestring):
411                print(self.debug % s)
412            elif hasattr(self.debug, 'write'):
413                self.debug.write(s + '\n')
414            elif callable(self.debug):
415                self.debug(s)
416            else:
417                print(s)
418
419    def _escape_qualified_name(self, s):
420        """Escape a qualified name.
421
422        Escapes the name for use as an SQL identifier, unless the
423        name contains a dot, in which case the name is ambiguous
424        (could be a qualified name or just a name with a dot in it)
425        and must be quoted manually by the caller.
426        """
427        if '.' not in s:
428            s = self.escape_identifier(s)
429        return s
430
431    @staticmethod
432    def _make_bool(d):
433        """Get boolean value corresponding to d."""
434        return bool(d) if get_bool() else ('t' if d else 'f')
435
436    _bool_true_values = frozenset('t true 1 y yes on'.split())
437
438    def _prepare_bool(self, d):
439        """Prepare a boolean parameter."""
440        if isinstance(d, basestring):
441            if not d:
442                return None
443            d = d.lower() in self._bool_true_values
444        return 't' if d else 'f'
445
446    _date_literals = frozenset('current_date current_time'
447        ' current_timestamp localtime localtimestamp'.split())
448
449    def _prepare_date(self, d):
450        """Prepare a date parameter."""
451        if not d:
452            return None
453        if isinstance(d, basestring) and d.lower() in self._date_literals:
454            raise ValueError
455        return d
456
457    _num_types = frozenset('int float num money'
458        ' int2 int4 int8 float4 float8 numeric money'.split())
459
460    def _prepare_num(self, d):
461        """Prepare a numeric parameter."""
462        if not d and d != 0:
463            return None
464        return d
465
466    def _prepare_bytea(self, d):
467        """Prepare a bytea parameter."""
468        return self.escape_bytea(d)
469
470    _prepare_funcs = dict(  # quote methods for each type
471        bool=_prepare_bool, date=_prepare_date,
472        int=_prepare_num, num=_prepare_num, float=_prepare_num,
473        money=_prepare_num, bytea=_prepare_bytea)
474
475    def _prepare_param(self, value, typ, params):
476        """Prepare and add a parameter to the list."""
477        if value is not None and typ != 'text':
478            prepare = self._prepare_funcs[typ]
479            try:
480                value = prepare(self, value)
481            except ValueError:
482                return value
483        params.append(value)
484        return '$%d' % len(params)
485
486    def _list_params(self, params):
487        """Create a human readable parameter list."""
488        return ', '.join('$%d=%r' % (n, v) for n, v in enumerate(params, 1))
489
490    @staticmethod
491    def _prepare_qualified_param(name, param):
492        """Quote parameter representing a qualified name.
493
494        Escapes the name for use as an SQL parameter, unless the
495        name contains a dot, in which case the name is ambiguous
496        (could be a qualified name or just a name with a dot in it)
497        and must be quoted manually by the caller.
498
499        """
500        if isinstance(param, int):
501            param = "$%d" % param
502        if '.' not in name:
503            param = 'quote_ident(%s)' % (param,)
504        return param
505
506    # Public methods
507
508    # escape_string and escape_bytea exist as methods,
509    # so we define unescape_bytea as a method as well
510    unescape_bytea = staticmethod(unescape_bytea)
511
512    def close(self):
513        """Close the database connection."""
514        # Wraps shared library function so we can track state.
515        if self._closeable:
516            if self.db:
517                self.db.close()
518                self.db = None
519            else:
520                raise _int_error('Connection already closed')
521
522    def reset(self):
523        """Reset connection with current parameters.
524
525        All derived queries and large objects derived from this connection
526        will not be usable after this call.
527
528        """
529        if self.db:
530            self.db.reset()
531        else:
532            raise _int_error('Connection already closed')
533
534    def reopen(self):
535        """Reopen connection to the database.
536
537        Used in case we need another connection to the same database.
538        Note that we can still reopen a database that we have closed.
539
540        """
541        # There is no such shared library function.
542        if self._closeable:
543            db = connect(*self._args[0], **self._args[1])
544            if self.db:
545                self.db.close()
546            self.db = db
547
548    def begin(self, mode=None):
549        """Begin a transaction."""
550        qstr = 'BEGIN'
551        if mode:
552            qstr += ' ' + mode
553        return self.query(qstr)
554
555    start = begin
556
557    def commit(self):
558        """Commit the current transaction."""
559        return self.query('COMMIT')
560
561    end = commit
562
563    def rollback(self, name=None):
564        """Roll back the current transaction."""
565        qstr = 'ROLLBACK'
566        if name:
567            qstr += ' TO ' + name
568        return self.query(qstr)
569
570    abort = rollback
571
572    def savepoint(self, name):
573        """Define a new savepoint within the current transaction."""
574        return self.query('SAVEPOINT ' + name)
575
576    def release(self, name):
577        """Destroy a previously defined savepoint."""
578        return self.query('RELEASE ' + name)
579
580    def get_parameter(self, parameter):
581        """Get the value of a run-time parameter.
582
583        If the parameter is a string, the return value will also be a string
584        that is the current setting of the run-time parameter with that name.
585
586        You can get several parameters at once by passing a list, set or dict.
587        When passing a list of parameter names, the return value will be a
588        corresponding list of parameter settings.  When passing a set of
589        parameter names, a new dict will be returned, mapping these parameter
590        names to their settings.  Finally, if you pass a dict as parameter,
591        its values will be set to the current parameter settings corresponding
592        to its keys.
593
594        By passing the special name 'all' as the parameter, you can get a dict
595        of all existing configuration parameters.
596        """
597        if isinstance(parameter, basestring):
598            parameter = [parameter]
599            values = None
600        elif isinstance(parameter, (list, tuple)):
601            values = []
602        elif isinstance(parameter, (set, frozenset)):
603            values = {}
604        elif isinstance(parameter, dict):
605            values = parameter
606        else:
607            raise TypeError(
608                'The parameter must be a string, list, set or dict')
609        if not parameter:
610            raise TypeError('No parameter has been specified')
611        params = {} if isinstance(values, dict) else []
612        for key in parameter:
613            param = key.strip().lower() if isinstance(
614                key, basestring) else None
615            if not param:
616                raise TypeError('Invalid parameter')
617            if param == 'all':
618                q = 'SHOW ALL'
619                values = self.db.query(q).getresult()
620                values = dict(value[:2] for value in values)
621                break
622            if isinstance(values, dict):
623                params[param] = key
624            else:
625                params.append(param)
626        else:
627            for param in params:
628                q = 'SHOW %s' % (param,)
629                value = self.db.query(q).getresult()[0][0]
630                if values is None:
631                    values = value
632                elif isinstance(values, list):
633                    values.append(value)
634                else:
635                    values[params[param]] = value
636        return values
637
638    def set_parameter(self, parameter, value=None, local=False):
639        """Set the value of a run-time parameter.
640
641        If the parameter and the value are strings, the run-time parameter
642        will be set to that value.  If no value or None is passed as a value,
643        then the run-time parameter will be restored to its default value.
644
645        You can set several parameters at once by passing a list of parameter
646        names, together with a single value that all parameters should be
647        set to or with a corresponding list of values.  You can also pass
648        the parameters as a set if you only provide a single value.
649        Finally, you can pass a dict with parameter names as keys.  In this
650        case, you should not pass a value, since the values for the parameters
651        will be taken from the dict.
652
653        By passing the special name 'all' as the parameter, you can reset
654        all existing settable run-time parameters to their default values.
655
656        If you set local to True, then the command takes effect for only the
657        current transaction.  After commit() or rollback(), the session-level
658        setting takes effect again.  Setting local to True will appear to
659        have no effect if it is executed outside a transaction, since the
660        transaction will end immediately.
661        """
662        if isinstance(parameter, basestring):
663            parameter = {parameter: value}
664        elif isinstance(parameter, (list, tuple)):
665            if isinstance(value, (list, tuple)):
666                parameter = dict(zip(parameter, value))
667            else:
668                parameter = dict.fromkeys(parameter, value)
669        elif isinstance(parameter, (set, frozenset)):
670            if isinstance(value, (list, tuple, set, frozenset)):
671                value = set(value)
672                if len(value) == 1:
673                    value = value.pop()
674            if not(value is None or isinstance(value, basestring)):
675                raise ValueError('A single value must be specified'
676                    ' when parameter is a set')
677            parameter = dict.fromkeys(parameter, value)
678        elif isinstance(parameter, dict):
679            if value is not None:
680                raise ValueError('A value must not be specified'
681                    ' when parameter is a dictionary')
682        else:
683            raise TypeError(
684                'The parameter must be a string, list, set or dict')
685        if not parameter:
686            raise TypeError('No parameter has been specified')
687        params = {}
688        for key, value in parameter.items():
689            param = key.strip().lower() if isinstance(
690                key, basestring) else None
691            if not param:
692                raise TypeError('Invalid parameter')
693            if param == 'all':
694                if value is not None:
695                    raise ValueError('A value must ot be specified'
696                        " when parameter is 'all'")
697                params = {'all': None}
698                break
699            params[param] = value
700        local = ' LOCAL' if local else ''
701        for param, value in params.items():
702            if value is None:
703                q = 'RESET%s %s' % (local, param)
704            else:
705                q = 'SET%s %s TO %s' % (local, param, value)
706            self._do_debug(q)
707            self.db.query(q)
708
709    def query(self, qstr, *args):
710        """Execute a SQL command string.
711
712        This method simply sends a SQL query to the database.  If the query is
713        an insert statement that inserted exactly one row into a table that
714        has OIDs, the return value is the OID of the newly inserted row.
715        If the query is an update or delete statement, or an insert statement
716        that did not insert exactly one row in a table with OIDs, then the
717        number of rows affected is returned as a string.  If it is a statement
718        that returns rows as a result (usually a select statement, but maybe
719        also an "insert/update ... returning" statement), this method returns
720        a Query object that can be accessed via getresult() or dictresult()
721        or simply printed.  Otherwise, it returns `None`.
722
723        The query can contain numbered parameters of the form $1 in place
724        of any data constant.  Arguments given after the query string will
725        be substituted for the corresponding numbered parameter.  Parameter
726        values can also be given as a single list or tuple argument.
727        """
728        # Wraps shared library function for debugging.
729        if not self.db:
730            raise _int_error('Connection is not valid')
731        self._do_debug(qstr)
732        return self.db.query(qstr, args)
733
734    def pkey(self, table, composite=False, flush=False):
735        """Get or set the primary key of a table.
736
737        Single primary keys are returned as strings unless you
738        set the composite flag.  Composite primary keys are always
739        represented as tuples.  Note that this raises a KeyError
740        if the table does not have a primary key.
741
742        If flush is set then the internal cache for primary keys will
743        be flushed.  This may be necessary after the database schema or
744        the search path has been changed.
745        """
746        pkeys = self._pkeys
747        if flush:
748            pkeys.clear()
749            self._do_debug('The pkey cache has been flushed')
750        try:  # cache lookup
751            pkey = pkeys[table]
752        except KeyError:  # cache miss, check the database
753            q = ("SELECT a.attname, a.attnum, i.indkey FROM pg_index i"
754                " JOIN pg_attribute a ON a.attrelid = i.indrelid"
755                " AND a.attnum = ANY(i.indkey)"
756                " AND NOT a.attisdropped"
757                " WHERE i.indrelid=%s::regclass"
758                " AND i.indisprimary ORDER BY a.attnum") % (
759                    self._prepare_qualified_param(table, 1),)
760            pkey = self.db.query(q, (table,)).getresult()
761            if not pkey:
762                raise KeyError('Table %s has no primary key' % table)
763            # we want to use the order defined in the primary key index here,
764            # not the order as defined by the columns in the table
765            if len(pkey) > 1:
766                indkey = [int(k) for k in pkey[0][2].split()]
767                pkey = sorted(pkey, key=lambda row: indkey.index(row[1]))
768                pkey = tuple(row[0] for row in pkey)
769            else:
770                pkey = pkey[0][0]
771            pkeys[table] = pkey  # cache it
772        if composite and not isinstance(pkey, tuple):
773            pkey = (pkey,)
774        return pkey
775
776    def get_databases(self):
777        """Get list of databases in the system."""
778        return [s[0] for s in
779            self.db.query('SELECT datname FROM pg_database').getresult()]
780
781    def get_relations(self, kinds=None):
782        """Get list of relations in connected database of specified kinds.
783
784        If kinds is None or empty, all kinds of relations are returned.
785        Otherwise kinds can be a string or sequence of type letters
786        specifying which kind of relations you want to list.
787        """
788        where = " AND r.relkind IN (%s)" % ','.join(
789            ["'%s'" % k for k in kinds]) if kinds else ''
790        q = ("SELECT quote_ident(s.nspname)||'.'||quote_ident(r.relname)"
791            " FROM pg_class r"
792            " JOIN pg_namespace s ON s.oid = r.relnamespace"
793            " WHERE s.nspname NOT SIMILAR"
794            " TO 'pg/_%%|information/_schema' ESCAPE '/' %s"
795            " ORDER BY s.nspname, r.relname") % where
796        return [r[0] for r in self.db.query(q).getresult()]
797
798    def get_tables(self):
799        """Return list of tables in connected database."""
800        return self.get_relations('r')
801
802    def get_attnames(self, table, flush=False):
803        """Given the name of a table, dig out the set of attribute names.
804
805        Returns a read-only dictionary of attribute names (the names are
806        the keys, the values are the names of the attributes' types)
807        with the column names in the proper order if you iterate over it.
808
809        If flush is set, then the internal cache for attribute names will
810        be flushed. This may be necessary after the database schema or
811        the search path has been changed.
812
813        By default, only a limited number of simple types will be returned.
814        You can get the regular types after calling use_regtypes(True).
815        """
816        attnames = self._attnames
817        if flush:
818            attnames.clear()
819            self._do_debug('The attnames cache has been flushed')
820        try:  # cache lookup
821            names = attnames[table]
822        except KeyError:  # cache miss, check the database
823            q = ("SELECT a.attname, t.typname%s"
824                " FROM pg_attribute a"
825                " JOIN pg_type t ON t.oid = a.atttypid"
826                " WHERE a.attrelid = %s::regclass"
827                " AND (a.attnum > 0 OR a.attname = 'oid')"
828                " AND NOT a.attisdropped ORDER BY a.attnum") % (
829                    '::regtype' if self._regtypes else '',
830                    self._prepare_qualified_param(table, 1))
831            names = self.db.query(q, (table,)).getresult()
832            if not self._regtypes:
833                names = ((name, _simpletype(typ)) for name, typ in names)
834            names = AttrDict(names)
835            attnames[table] = names  # cache it
836        return names
837
838    def use_regtypes(self, regtypes=None):
839        """Use regular type names instead of simplified type names."""
840        if regtypes is None:
841            return self._regtypes
842        else:
843            regtypes = bool(regtypes)
844            if regtypes != self._regtypes:
845                self._regtypes = regtypes
846                self._attnames.clear()
847            return regtypes
848
849    def has_table_privilege(self, table, privilege='select'):
850        """Check whether current user has specified table privilege."""
851        privilege = privilege.lower()
852        try:  # ask cache
853            return self._privileges[(table, privilege)]
854        except KeyError:  # cache miss, ask the database
855            q = "SELECT has_table_privilege(%s, $2)" % (
856                self._prepare_qualified_param(table, 1),)
857            q = self.db.query(q, (table, privilege))
858            ret = q.getresult()[0][0] == self._make_bool(True)
859            self._privileges[(table, privilege)] = ret  # cache it
860            return ret
861
862    def get(self, table, row, keyname=None):
863        """Get a row from a database table or view.
864
865        This method is the basic mechanism to get a single row.  It assumes
866        that the keyname specifies a unique row.  It must be the name of a
867        single column or a tuple of column names.  If the keyname is not
868        specified, then the primary key for the table is used.
869
870        If row is a dictionary, then the value for the key is taken from it.
871        Otherwise, the row must be a single value or a tuple of values
872        corresponding to the passed keyname or primary key.  The fetched row
873        from the table will be returned as a new dictionary or used to replace
874        the existing values when row was passed as aa dictionary.
875
876        The OID is also put into the dictionary if the table has one, but
877        in order to allow the caller to work with multiple tables, it is
878        munged as "oid(table)" using the actual name of the table.
879        """
880        if table.endswith('*'):  # hint for descendant tables can be ignored
881            table = table[:-1].rstrip()
882        attnames = self.get_attnames(table)
883        qoid = _oid_key(table) if 'oid' in attnames else None
884        if keyname and isinstance(keyname, basestring):
885            keyname = (keyname,)
886        if qoid and isinstance(row, dict) and qoid in row and 'oid' not in row:
887            row['oid'] = row[qoid]
888        if not keyname:
889            try:  # if keyname is not specified, try using the primary key
890                keyname = self.pkey(table, True)
891            except KeyError:  # the table has no primary key
892                # try using the oid instead
893                if qoid and isinstance(row, dict) and 'oid' in row:
894                    keyname = ('oid',)
895                else:
896                    raise _prg_error('Table %s has no primary key' % table)
897            else:  # the table has a primary key
898                # check whether all key columns have values
899                if isinstance(row, dict) and not set(keyname).issubset(row):
900                    # try using the oid instead
901                    if qoid and 'oid' in row:
902                        keyname = ('oid',)
903                    else:
904                        raise KeyError(
905                            'Missing value in row for specified keyname')
906        if not isinstance(row, dict):
907            if not isinstance(row, (tuple, list)):
908                row = [row]
909            if len(keyname) != len(row):
910                raise KeyError(
911                    'Differing number of items in keyname and row')
912            row = dict(zip(keyname, row))
913        params = []
914        param = partial(self._prepare_param, params=params)
915        col = self.escape_identifier
916        what = 'oid, *' if qoid else '*'
917        where = ' AND '.join('%s = %s' % (
918            col(k), param(row[k], attnames[k])) for k in keyname)
919        if 'oid' in row:
920            if qoid:
921                row[qoid] = row['oid']
922            del row['oid']
923        q = 'SELECT %s FROM %s WHERE %s LIMIT 1' % (
924            what, self._escape_qualified_name(table), where)
925        self._do_debug(q, params)
926        q = self.db.query(q, params)
927        res = q.dictresult()
928        if not res:
929            raise _db_error('No such record in %s\nwhere %s\nwith %s' % (
930                table, where, self._list_params(params)))
931        for n, value in res[0].items():
932            if qoid and n == 'oid':
933                n = qoid
934            elif value is not None and attnames.get(n) == 'bytea':
935                value = self.unescape_bytea(value)
936            row[n] = value
937        return row
938
939    def insert(self, table, row=None, **kw):
940        """Insert a row into a database table.
941
942        This method inserts a row into a table.  The name of the table must
943        be passed as the first parameter.  The other parameters are used for
944        providing the data of the row that shall be inserted into the table.
945        If a dictionary is supplied as the second parameter, it starts with
946        that.  Otherwise it uses a blank dictionary. Either way the dictionary
947        is updated from the keywords.
948
949        The dictionary is then reloaded with the values actually inserted in
950        order to pick up values modified by rules, triggers, etc.
951        """
952        if table.endswith('*'):  # hint for descendant tables can be ignored
953            table = table[:-1].rstrip()
954        if row is None:
955            row = {}
956        row.update(kw)
957        if 'oid' in row:
958            del row['oid']  # do not insert oid
959        attnames = self.get_attnames(table)
960        qoid = _oid_key(table) if 'oid' in attnames else None
961        params = []
962        param = partial(self._prepare_param, params=params)
963        col = self.escape_identifier
964        names, values = [], []
965        for n in attnames:
966            if n in row:
967                names.append(col(n))
968                values.append(param(row[n], attnames[n]))
969        names, values = ', '.join(names), ', '.join(values)
970        ret = 'oid, *' if qoid else '*'
971        q = 'INSERT INTO %s (%s) VALUES (%s) RETURNING %s' % (
972            self._escape_qualified_name(table), names, values, ret)
973        self._do_debug(q, params)
974        q = self.db.query(q, params)
975        res = q.dictresult()
976        if res:  # this should always be true
977            for n, value in res[0].items():
978                if qoid and n == 'oid':
979                    n = qoid
980                elif value is not None and attnames.get(n) == 'bytea':
981                    value = self.unescape_bytea(value)
982                row[n] = value
983        return row
984
985    def update(self, table, row=None, **kw):
986        """Update an existing row in a database table.
987
988        Similar to insert but updates an existing row.  The update is based
989        on the primary key of the table or the OID value as munged by get
990        or passed as keyword.
991
992        The dictionary is then modified to reflect any changes caused by the
993        update due to triggers, rules, default values, etc.
994        """
995        if table.endswith('*'):
996            table = table[:-1].rstrip()  # need parent table name
997        attnames = self.get_attnames(table)
998        qoid = _oid_key(table) if 'oid' in attnames else None
999        if row is None:
1000            row = {}
1001        elif 'oid' in row:
1002            del row['oid']  # only accept oid key from named args for safety
1003        row.update(kw)
1004        if qoid and qoid in row and 'oid' not in row:
1005            row['oid'] = row[qoid]
1006        try:  # try using the primary key
1007            keyname = self.pkey(table, True)
1008        except KeyError:  # the table has no primary key
1009            # try using the oid instead
1010            if qoid and 'oid' in row:
1011                keyname = ('oid',)
1012            else:
1013                raise _prg_error('Table %s has no primary key' % table)
1014        else:  # the table has a primary key
1015            # check whether all key columns have values
1016            if not set(keyname).issubset(row):
1017                # try using the oid instead
1018                if qoid and 'oid' in row:
1019                    keyname = ('oid',)
1020                else:
1021                    raise KeyError('Missing primary key in row')
1022        params = []
1023        param = partial(self._prepare_param, params=params)
1024        col = self.escape_identifier
1025        where = ' AND '.join('%s = %s' % (
1026            col(k), param(row[k], attnames[k])) for k in keyname)
1027        if 'oid' in row:
1028            if qoid:
1029                row[qoid] = row['oid']
1030            del row['oid']
1031        values = []
1032        keyname = set(keyname)
1033        for n in attnames:
1034            if n in row and n not in keyname:
1035                values.append('%s = %s' % (col(n), param(row[n], attnames[n])))
1036        if not values:
1037            return row
1038        values = ', '.join(values)
1039        ret = 'oid, *' if qoid else '*'
1040        q = 'UPDATE %s SET %s WHERE %s RETURNING %s' % (
1041            self._escape_qualified_name(table), values, where, ret)
1042        self._do_debug(q, params)
1043        q = self.db.query(q, params)
1044        res = q.dictresult()
1045        if res:  # may be empty when row does not exist
1046            for n, value in res[0].items():
1047                if qoid and n == 'oid':
1048                    n = qoid
1049                elif value is not None and attnames.get(n) == 'bytea':
1050                    value = self.unescape_bytea(value)
1051                row[n] = value
1052        return row
1053
1054    def upsert(self, table, row=None, **kw):
1055        """Insert a row into a database table with conflict resolution
1056
1057        This method inserts a row into a table, but instead of raising a
1058        ProgrammingError exception in case a row with the same primary key
1059        already exists, an update will be executed instead.  This will be
1060        performed as a single atomic operation on the database, so race
1061        conditions can be avoided.
1062
1063        Like the insert method, the first parameter is the name of the
1064        table and the second parameter can be used to pass the values to
1065        be inserted as a dictionary.
1066
1067        Unlike the insert und update statement, keyword parameters are not
1068        used to modify the dictionary, but to specify which columns shall
1069        be updated in case of a conflict, and in which way:
1070
1071        A value of False or None means the column shall not be updated,
1072        a value of True means the column shall be updated with the value
1073        that has been proposed for insertion, i.e. has been passed as value
1074        in the dictionary.  Columns that are not specified by keywords but
1075        appear as keys in the dictionary are also updated like in the case
1076        keywords had been passed with the value True.
1077
1078        So if in the case of a conflict you want to update every column that
1079        has been passed in the dictionary row , you would call upsert(table, row).
1080        If you don't want to do anything in case of a conflict, i.e. leave
1081        the existing row as it is, call upsert(table, row, **dict.fromkeys(row)).
1082
1083        If you need more fine-grained control of what gets updated, you can
1084        also pass strings in the keyword parameters.  These strings will
1085        be used as SQL expressions for the update columns.  In these
1086        expressions you can refer to the value that already exists in
1087        the table by prefixing the column name with "included.", and to
1088        the value that has been proposed for insertion by prefixing the
1089        column name with the "excluded."
1090
1091        The dictionary is modified in any case to reflect the values in
1092        the database after the operation has completed.
1093
1094        Note: The method uses the PostgreSQL "upsert" feature which is
1095        only available since PostgreSQL 9.5.
1096        """
1097        if table.endswith('*'):  # hint for descendant tables can be ignored
1098            table = table[:-1].rstrip()
1099        if row is None:
1100            row = {}
1101        if 'oid' in row:
1102            del row['oid']  # do not insert oid
1103        if 'oid' in kw:
1104            del kw['oid']  # do not update oid
1105        attnames = self.get_attnames(table)
1106        qoid = _oid_key(table) if 'oid' in attnames else None
1107        params = []
1108        param = partial(self._prepare_param,params=params)
1109        col = self.escape_identifier
1110        names, values, updates = [], [], []
1111        for n in attnames:
1112            if n in row:
1113                names.append(col(n))
1114                values.append(param(row[n], attnames[n]))
1115        names, values = ', '.join(names), ', '.join(values)
1116        try:
1117            keyname = self.pkey(table, True)
1118        except KeyError:
1119            raise _prg_error('Table %s has no primary key' % table)
1120        target = ', '.join(col(k) for k in keyname)
1121        update = []
1122        keyname = set(keyname)
1123        keyname.add('oid')
1124        for n in attnames:
1125            if n not in keyname:
1126                value = kw.get(n, True)
1127                if value:
1128                    if not isinstance(value, basestring):
1129                        value = 'excluded.%s' % col(n)
1130                    update.append('%s = %s' % (col(n), value))
1131        if not values:
1132            return row
1133        do = 'update set %s' % ', '.join(update) if update else 'nothing'
1134        ret = 'oid, *' if qoid else '*'
1135        q = ('INSERT INTO %s AS included (%s) VALUES (%s)'
1136            ' ON CONFLICT (%s) DO %s RETURNING %s') % (
1137                self._escape_qualified_name(table), names, values,
1138                target, do, ret)
1139        self._do_debug(q, params)
1140        try:
1141            q = self.db.query(q, params)
1142        except ProgrammingError:
1143            if self.server_version < 90500:
1144                raise _prg_error(
1145                    'Upsert operation is not supported by PostgreSQL version')
1146            raise  # re-raise original error
1147        res = q.dictresult()
1148        if res:  # may be empty with "do nothing"
1149            for n, value in res[0].items():
1150                if qoid and n == 'oid':
1151                    n = qoid
1152                elif value is not None and attnames.get(n) == 'bytea':
1153                    value = self.unescape_bytea(value)
1154                row[n] = value
1155        else:
1156            self.get(table, row)
1157        return row
1158
1159    def clear(self, table, row=None):
1160        """Clear all the attributes to values determined by the types.
1161
1162        Numeric types are set to 0, Booleans are set to false, and everything
1163        else is set to the empty string.  If the row argument is present,
1164        it is used as the row dictionary and any entries matching attribute
1165        names are cleared with everything else left unchanged.
1166        """
1167        # At some point we will need a way to get defaults from a table.
1168        if row is None:
1169            row = {}  # empty if argument is not present
1170        attnames = self.get_attnames(table)
1171        for n, t in attnames.items():
1172            if n == 'oid':
1173                continue
1174            if t in self._num_types:
1175                row[n] = 0
1176            elif t == 'bool':
1177                row[n] = self._make_bool(False)
1178            else:
1179                row[n] = ''
1180        return row
1181
1182    def delete(self, table, row=None, **kw):
1183        """Delete an existing row in a database table.
1184
1185        This method deletes the row from a table.  It deletes based on the
1186        primary key of the table or the OID value as munged by get() or
1187        passed as keyword.
1188
1189        The return value is the number of deleted rows (i.e. 0 if the row
1190        did not exist and 1 if the row was deleted).
1191
1192        Note that if the row cannot be deleted because e.g. it is still
1193        referenced by another table, this method raises a ProgrammingError.
1194        """
1195        if table.endswith('*'):  # hint for descendant tables can be ignored
1196            table = table[:-1].rstrip()
1197        attnames = self.get_attnames(table)
1198        qoid = _oid_key(table) if 'oid' in attnames else None
1199        if row is None:
1200            row = {}
1201        elif 'oid' in row:
1202            del row['oid']  # only accept oid key from named args for safety
1203        row.update(kw)
1204        if qoid and qoid in row and 'oid' not in row:
1205            row['oid'] = row[qoid]
1206        try:  # try using the primary key
1207            keyname = self.pkey(table, True)
1208        except KeyError:  # the table has no primary key
1209            # try using the oid instead
1210            if qoid and 'oid' in row:
1211                keyname = ('oid',)
1212            else:
1213                raise _prg_error('Table %s has no primary key' % table)
1214        else:  # the table has a primary key
1215            # check whether all key columns have values
1216            if not set(keyname).issubset(row):
1217                # try using the oid instead
1218                if qoid and 'oid' in row:
1219                    keyname = ('oid',)
1220                else:
1221                    raise KeyError('Missing primary key in row')
1222        params = []
1223        param = partial(self._prepare_param, params=params)
1224        col = self.escape_identifier
1225        where = ' AND '.join('%s = %s' % (
1226            col(k), param(row[k], attnames[k])) for k in keyname)
1227        if 'oid' in row:
1228            if qoid:
1229                row[qoid] = row['oid']
1230            del row['oid']
1231        q = 'DELETE FROM %s WHERE %s' % (
1232            self._escape_qualified_name(table), where)
1233        self._do_debug(q, params)
1234        res = self.db.query(q, params)
1235        return int(res)
1236
1237    def truncate(self, table, restart=False, cascade=False, only=False):
1238        """Empty a table or set of tables.
1239
1240        This method quickly removes all rows from the given table or set
1241        of tables.  It has the same effect as an unqualified DELETE on each
1242        table, but since it does not actually scan the tables it is faster.
1243        Furthermore, it reclaims disk space immediately, rather than requiring
1244        a subsequent VACUUM operation. This is most useful on large tables.
1245
1246        If restart is set to True, sequences owned by columns of the truncated
1247        table(s) are automatically restarted.  If cascade is set to True, it
1248        also truncates all tables that have foreign-key references to any of
1249        the named tables.  If the parameter only is not set to True, all the
1250        descendant tables (if any) will also be truncated. Optionally, a '*'
1251        can be specified after the table name to explicitly indicate that
1252        descendant tables are included.
1253        """
1254        if isinstance(table, basestring):
1255            only = {table: only}
1256            table = [table]
1257        elif isinstance(table, (list, tuple)):
1258            if isinstance(only, (list, tuple)):
1259                only = dict(zip(table, only))
1260            else:
1261                only = dict.fromkeys(table, only)
1262        elif isinstance(table, (set, frozenset)):
1263            only = dict.fromkeys(table, only)
1264        else:
1265            raise TypeError('The table must be a string, list or set')
1266        if not (restart is None or isinstance(restart, (bool, int))):
1267            raise TypeError('Invalid type for the restart option')
1268        if not (cascade is None or isinstance(cascade, (bool, int))):
1269            raise TypeError('Invalid type for the cascade option')
1270        tables = []
1271        for t in table:
1272            u = only.get(t)
1273            if not (u is None or isinstance(u, (bool, int))):
1274                raise TypeError('Invalid type for the only option')
1275            if t.endswith('*'):
1276                if u:
1277                    raise ValueError(
1278                        'Contradictory table name and only options')
1279                t = t[:-1].rstrip()
1280            t = self._escape_qualified_name(t)
1281            if u:
1282                t = 'ONLY %s' % t
1283            tables.append(t)
1284        q = ['TRUNCATE', ', '.join(tables)]
1285        if restart:
1286            q.append('RESTART IDENTITY')
1287        if cascade:
1288            q.append('CASCADE')
1289        q = ' '.join(q)
1290        self._do_debug(q)
1291        return self.db.query(q)
1292
1293    def get_as_list(self, table, what=None, where=None,
1294            order=None, limit=None, offset=None, scalar=False):
1295        """Get a table as a list.
1296
1297        This gets a convenient representation of the table as a list
1298        of named tuples in Python.  You only need to pass the name of
1299        the table (or any other SQL expression returning rows).  Note that
1300        by default this will return the full content of the table which
1301        can be huge and overflow your memory.  However, you can control
1302        the amount of data returned using the other optional parameters.
1303
1304        The parameter 'what' can restrict the query to only return a
1305        subset of the table columns.  It can be a string, list or a tuple.
1306        The parameter 'where' can restrict the query to only return a
1307        subset of the table rows.  It can be a string, list or a tuple
1308        of SQL expressions that all need to be fulfilled.  The parameter
1309        'order' specifies the ordering of the rows.  It can also be a
1310        other string, list or a tuple.  If no ordering is specified,
1311        the result will be ordered by the primary key(s) or all columns
1312        if no primary key exists.  You can set 'order' to False if you
1313        don't care about the ordering.  The parameters 'limit' and 'offset'
1314        can be integers specifying the maximum number of rows returned
1315        and a number of rows skipped over.
1316
1317        If you set the 'scalar' option to True, then instead of the
1318        named tuples you will get the first items of these tuples.
1319        This is useful if the result has only one column anyway.
1320        """
1321        if not table:
1322            raise TypeError('The table name is missing')
1323        if what:
1324            if isinstance(what, (list, tuple)):
1325                what = ', '.join(map(str, what))
1326            if order is None:
1327                order = what
1328        else:
1329            what = '*'
1330        q = ['SELECT', what, 'FROM', table]
1331        if where:
1332            if isinstance(where, (list, tuple)):
1333                where = ' AND '.join(map(str, where))
1334            q.extend(['WHERE', where])
1335        if order is None:
1336            try:
1337                order = self.pkey(table, True)
1338            except (KeyError, ProgrammingError):
1339                try:
1340                    order = list(self.get_attnames(table))
1341                except (KeyError, ProgrammingError):
1342                    pass
1343        if order:
1344            if isinstance(order, (list, tuple)):
1345                order = ', '.join(map(str, order))
1346            q.extend(['ORDER BY', order])
1347        if limit:
1348            q.append('LIMIT %d' % limit)
1349        if offset:
1350            q.append('OFFSET %d' % offset)
1351        q = ' '.join(q)
1352        self._do_debug(q)
1353        q = self.db.query(q)
1354        res = q.namedresult()
1355        if res and scalar:
1356            res = [row[0] for row in res]
1357        return res
1358
1359    def get_as_dict(self, table, keyname=None, what=None, where=None,
1360            order=None, limit=None, offset=None, scalar=False):
1361        """Get a table as a dictionary.
1362
1363        This method is similar to get_as_list(), but returns the table
1364        as a Python dict instead of a Python list, which can be even
1365        more convenient. The primary key column(s) of the table will
1366        be used as the keys of the dictionary, while the other column(s)
1367        will be the corresponding values.  The keys will be named tuples
1368        if the table has a composite primary key.  The rows will be also
1369        named tuples unless the 'scalar' option has been set to True.
1370        With the optional parameter 'keyname' you can specify an alternative
1371        set of columns to be used as the keys of the dictionary.  It must
1372        be set as a string, list or a tuple.
1373
1374        If the Python version supports it, the dictionary will be an
1375        OrderedDict using the order specified with the 'order' parameter
1376        or the key column(s) if not specified.  You can set 'order' to False
1377        if you don't care about the ordering.  In this case the returned
1378        dictionary will be an ordinary one.
1379        """
1380        if not table:
1381            raise TypeError('The table name is missing')
1382        if not keyname:
1383            try:
1384                keyname = self.pkey(table, True)
1385            except (KeyError, ProgrammingError):
1386                raise _prg_error('Table %s has no primary key' % table)
1387        if isinstance(keyname, basestring):
1388            keyname = [keyname]
1389        elif not isinstance(keyname, (list, tuple)):
1390            raise KeyError('The keyname must be a string, list or tuple')
1391        if what:
1392            if isinstance(what, (list, tuple)):
1393                what = ', '.join(map(str, what))
1394            if order is None:
1395                order = what
1396        else:
1397            what = '*'
1398        q = ['SELECT', what, 'FROM', table]
1399        if where:
1400            if isinstance(where, (list, tuple)):
1401                where = ' AND '.join(map(str, where))
1402            q.extend(['WHERE', where])
1403        if order is None:
1404            order = keyname
1405        if order:
1406            if isinstance(order, (list, tuple)):
1407                order = ', '.join(map(str, order))
1408            q.extend(['ORDER BY', order])
1409        if limit:
1410            q.append('LIMIT %d' % limit)
1411        if offset:
1412            q.append('OFFSET %d' % offset)
1413        q = ' '.join(q)
1414        self._do_debug(q)
1415        q = self.db.query(q)
1416        res = q.getresult()
1417        cls = OrderedDict if order else dict
1418        if not res:
1419            return cls()
1420        keyset = set(keyname)
1421        fields = q.listfields()
1422        if not keyset.issubset(fields):
1423            raise KeyError('Missing keyname in row')
1424        keyind, rowind = [], []
1425        for i, f in enumerate(fields):
1426            (keyind if f in keyset else rowind).append(i)
1427        keytuple = len(keyind) > 1
1428        getkey = itemgetter(*keyind)
1429        keys = map(getkey, res)
1430        if scalar:
1431            rowind = rowind[:1]
1432            rowtuple = False
1433        else:
1434            rowtuple = len(rowind) > 1
1435        if scalar or rowtuple:
1436            getrow = itemgetter(*rowind)
1437        else:
1438            rowind = rowind[0]
1439            getrow = lambda row: (row[rowind],)
1440            rowtuple = True
1441        rows = map(getrow, res)
1442        if keytuple or rowtuple:
1443            namedresult = get_namedresult()
1444            if keytuple:
1445                keys = namedresult(_MemoryQuery(keys, keyname))
1446            if rowtuple:
1447                fields = [f for f in fields if f not in keyset]
1448                rows = namedresult(_MemoryQuery(rows, fields))
1449        return cls(zip(keys, rows))
1450
1451    def notification_handler(self,
1452            event, callback, arg_dict=None, timeout=None, stop_event=None):
1453        """Get notification handler that will run the given callback."""
1454        return NotificationHandler(self,
1455            event, callback, arg_dict, timeout, stop_event)
1456
1457
1458# if run as script, print some information
1459
1460if __name__ == '__main__':
1461    print('PyGreSQL version' + version)
1462    print('')
1463    print(__doc__)
Note: See TracBrowser for help on using the repository browser.