~ubuntu-branches/debian/jessie/sqlalchemy/jessie

« back to all changes in this revision

Viewing changes to lib/sqlalchemy/dialects/postgresql/base.py

  • Committer: Package Import Robot
  • Author(s): Piotr Ożarowski, Jakub Wilk, Piotr Ożarowski
  • Date: 2013-07-06 20:53:52 UTC
  • mfrom: (1.4.23) (16.1.17 experimental)
  • Revision ID: package-import@ubuntu.com-20130706205352-ryppl1eto3illd79
Tags: 0.8.2-1
[ Jakub Wilk ]
* Use canonical URIs for Vcs-* fields.

[ Piotr Ożarowski ]
* New upstream release
* Upload to unstable
* Build depend on python3-all instead of -dev, extensions are not built for
  Python 3.X 

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# postgresql/base.py
2
 
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
 
2
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
3
3
#
4
4
# This module is part of SQLAlchemy and is released under
5
5
# the MIT License: http://www.opensource.org/licenses/mit-license.php
6
6
 
7
 
"""Support for the PostgreSQL database.
 
7
"""
 
8
.. dialect:: postgresql
 
9
    :name: PostgreSQL
8
10
 
9
 
For information on connecting using specific drivers, see the documentation
10
 
section regarding that driver.
11
11
 
12
12
Sequences/SERIAL
13
13
----------------
41
41
To force the usage of RETURNING by default off, specify the flag
42
42
``implicit_returning=False`` to :func:`.create_engine`.
43
43
 
 
44
.. _postgresql_isolation_level:
 
45
 
44
46
Transaction Isolation Level
45
47
---------------------------
46
48
 
47
 
:func:`.create_engine` accepts an ``isolation_level`` parameter which results
48
 
in the command ``SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
49
 
<level>`` being invoked for every new connection. Valid values for this
50
 
parameter are ``READ COMMITTED``, ``READ UNCOMMITTED``, ``REPEATABLE READ``,
51
 
and ``SERIALIZABLE``::
 
49
All Postgresql dialects support setting of transaction isolation level
 
50
both via a dialect-specific parameter ``isolation_level``
 
51
accepted by :func:`.create_engine`,
 
52
as well as the ``isolation_level`` argument as passed to :meth:`.Connection.execution_options`.
 
53
When using a non-psycopg2 dialect, this feature works by issuing the
 
54
command ``SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
 
55
<level>`` for each new connection.
 
56
 
 
57
To set isolation level using :func:`.create_engine`::
52
58
 
53
59
    engine = create_engine(
54
60
                    "postgresql+pg8000://scott:tiger@localhost/test",
55
61
                    isolation_level="READ UNCOMMITTED"
56
62
                )
57
63
 
58
 
When using the psycopg2 dialect, a psycopg2-specific method of setting
59
 
transaction isolation level is used, but the API of ``isolation_level``
60
 
remains the same - see :ref:`psycopg2_isolation`.
 
64
To set using per-connection execution options::
 
65
 
 
66
    connection = engine.connect()
 
67
    connection = connection.execution_options(isolation_level="READ COMMITTED")
 
68
 
 
69
Valid values for ``isolation_level`` include:
 
70
 
 
71
* ``READ COMMITTED``
 
72
* ``READ UNCOMMITTED``
 
73
* ``REPEATABLE READ``
 
74
* ``SERIALIZABLE``
 
75
 
 
76
The :mod:`~sqlalchemy.dialects.postgresql.psycopg2` dialect also offers the special level ``AUTOCOMMIT``.  See
 
77
:ref:`psycopg2_isolation_level` for details.
61
78
 
62
79
 
63
80
Remote / Cross-Schema Table Introspection
114
131
        where(table.c.name=='foo')
115
132
    print result.fetchall()
116
133
 
 
134
FROM ONLY ...
 
135
------------------------
 
136
 
 
137
The dialect supports PostgreSQL's ONLY keyword for targeting only a particular
 
138
table in an inheritance hierarchy. This can be used to produce the
 
139
``SELECT ... FROM ONLY``, ``UPDATE ONLY ...``, and ``DELETE FROM ONLY ...``
 
140
syntaxes. It uses SQLAlchemy's hints mechanism::
 
141
 
 
142
    # SELECT ... FROM ONLY ...
 
143
    result = table.select().with_hint(table, 'ONLY', 'postgresql')
 
144
    print result.fetchall()
 
145
 
 
146
    # UPDATE ONLY ...
 
147
    table.update(values=dict(foo='bar')).with_hint('ONLY',
 
148
                                                   dialect_name='postgresql')
 
149
 
 
150
    # DELETE FROM ONLY ...
 
151
    table.delete().with_hint('ONLY', dialect_name='postgresql')
117
152
 
118
153
.. _postgresql_indexes:
119
154
 
136
171
^^^^^^^^^^^^^^^^^
137
172
 
138
173
PostgreSQL allows the specification of an *operator class* for each column of
139
 
an index (see http://www.postgresql.org/docs/8.3/interactive/indexes-opclass.html).
140
 
The :class:`.Index` construct allows these to be specified via the ``postgresql_ops``
141
 
keyword argument::
 
174
an index (see
 
175
http://www.postgresql.org/docs/8.3/interactive/indexes-opclass.html).
 
176
The :class:`.Index` construct allows these to be specified via the
 
177
``postgresql_ops`` keyword argument::
142
178
 
143
179
    Index('my_index', my_table.c.id, my_table.c.data,
144
180
                            postgresql_ops={
150
186
    ``postgresql_ops`` keyword argument to :class:`.Index` construct.
151
187
 
152
188
Note that the keys in the ``postgresql_ops`` dictionary are the "key" name of
153
 
the :class:`.Column`, i.e. the name used to access it from the ``.c`` collection
154
 
of :class:`.Table`, which can be configured to be different than the actual
155
 
name of the column as expressed in the database.
 
189
the :class:`.Column`, i.e. the name used to access it from the ``.c``
 
190
collection of :class:`.Table`, which can be configured to be different than
 
191
the actual name of the column as expressed in the database.
156
192
 
157
193
Index Types
158
194
^^^^^^^^^^^^
159
195
 
160
 
PostgreSQL provides several index types: B-Tree, Hash, GiST, and GIN, as well as
161
 
the ability for users to create their own (see
 
196
PostgreSQL provides several index types: B-Tree, Hash, GiST, and GIN, as well
 
197
as the ability for users to create their own (see
162
198
http://www.postgresql.org/docs/8.3/static/indexes-types.html). These can be
163
199
specified on :class:`.Index` using the ``postgresql_using`` keyword argument::
164
200
 
170
206
 
171
207
"""
172
208
 
 
209
from collections import defaultdict
173
210
import re
174
211
 
175
 
from sqlalchemy import sql, schema, exc, util
176
 
from sqlalchemy.engine import default, reflection
177
 
from sqlalchemy.sql import compiler, expression, util as sql_util
178
 
from sqlalchemy import types as sqltypes
 
212
from ... import sql, schema, exc, util
 
213
from ...engine import default, reflection
 
214
from ...sql import compiler, expression, util as sql_util, operators
 
215
from ... import types as sqltypes
179
216
 
180
217
try:
181
218
    from uuid import UUID as _python_UUID
208
245
_FLOAT_TYPES = (700, 701, 1021, 1022)
209
246
_INT_TYPES = (20, 21, 23, 26, 1005, 1007, 1016)
210
247
 
 
248
 
211
249
class BYTEA(sqltypes.LargeBinary):
212
250
    __visit_name__ = 'BYTEA'
213
251
 
 
252
 
214
253
class DOUBLE_PRECISION(sqltypes.Float):
215
254
    __visit_name__ = 'DOUBLE_PRECISION'
216
255
 
 
256
 
217
257
class INET(sqltypes.TypeEngine):
218
258
    __visit_name__ = "INET"
219
259
PGInet = INET
220
260
 
 
261
 
221
262
class CIDR(sqltypes.TypeEngine):
222
263
    __visit_name__ = "CIDR"
223
264
PGCidr = CIDR
224
265
 
 
266
 
225
267
class MACADDR(sqltypes.TypeEngine):
226
268
    __visit_name__ = "MACADDR"
227
269
PGMacAddr = MACADDR
228
270
 
 
271
 
229
272
class TIMESTAMP(sqltypes.TIMESTAMP):
230
273
    def __init__(self, timezone=False, precision=None):
231
274
        super(TIMESTAMP, self).__init__(timezone=timezone)
237
280
        super(TIME, self).__init__(timezone=timezone)
238
281
        self.precision = precision
239
282
 
 
283
 
240
284
class INTERVAL(sqltypes.TypeEngine):
241
285
    """Postgresql INTERVAL type.
242
286
 
245
289
 
246
290
    """
247
291
    __visit_name__ = 'INTERVAL'
 
292
 
248
293
    def __init__(self, precision=None):
249
294
        self.precision = precision
250
295
 
258
303
 
259
304
PGInterval = INTERVAL
260
305
 
 
306
 
261
307
class BIT(sqltypes.TypeEngine):
262
308
    __visit_name__ = 'BIT'
 
309
 
263
310
    def __init__(self, length=None, varying=False):
264
311
        if not varying:
265
312
            # BIT without VARYING defaults to length 1
271
318
 
272
319
PGBit = BIT
273
320
 
 
321
 
274
322
class UUID(sqltypes.TypeEngine):
275
323
    """Postgresql UUID type.
276
324
 
295
343
         """
296
344
        if as_uuid and _python_UUID is None:
297
345
            raise NotImplementedError(
298
 
                    "This version of Python does not support the native UUID type."
299
 
                )
 
346
                "This version of Python does not support the native UUID type."
 
347
            )
300
348
        self.as_uuid = as_uuid
301
349
 
302
350
    def bind_processor(self, dialect):
321
369
 
322
370
PGUuid = UUID
323
371
 
324
 
class ARRAY(sqltypes.MutableType, sqltypes.Concatenable, sqltypes.TypeEngine):
 
372
 
 
373
class _Slice(expression.ColumnElement):
 
374
    __visit_name__ = 'slice'
 
375
    type = sqltypes.NULLTYPE
 
376
 
 
377
    def __init__(self, slice_, source_comparator):
 
378
        self.start = source_comparator._check_literal(
 
379
                            source_comparator.expr,
 
380
                            operators.getitem, slice_.start)
 
381
        self.stop = source_comparator._check_literal(
 
382
                            source_comparator.expr,
 
383
                            operators.getitem, slice_.stop)
 
384
 
 
385
 
 
386
class Any(expression.ColumnElement):
 
387
    """Represent the clause ``left operator ANY (right)``.  ``right`` must be
 
388
    an array expression.
 
389
 
 
390
    .. seealso::
 
391
 
 
392
        :class:`.postgresql.ARRAY`
 
393
 
 
394
        :meth:`.postgresql.ARRAY.Comparator.any` - ARRAY-bound method
 
395
 
 
396
    """
 
397
    __visit_name__ = 'any'
 
398
 
 
399
    def __init__(self, left, right, operator=operators.eq):
 
400
        self.type = sqltypes.Boolean()
 
401
        self.left = expression._literal_as_binds(left)
 
402
        self.right = right
 
403
        self.operator = operator
 
404
 
 
405
 
 
406
class All(expression.ColumnElement):
 
407
    """Represent the clause ``left operator ALL (right)``.  ``right`` must be
 
408
    an array expression.
 
409
 
 
410
    .. seealso::
 
411
 
 
412
        :class:`.postgresql.ARRAY`
 
413
 
 
414
        :meth:`.postgresql.ARRAY.Comparator.all` - ARRAY-bound method
 
415
 
 
416
    """
 
417
    __visit_name__ = 'all'
 
418
 
 
419
    def __init__(self, left, right, operator=operators.eq):
 
420
        self.type = sqltypes.Boolean()
 
421
        self.left = expression._literal_as_binds(left)
 
422
        self.right = right
 
423
        self.operator = operator
 
424
 
 
425
 
 
426
class array(expression.Tuple):
 
427
    """A Postgresql ARRAY literal.
 
428
 
 
429
    This is used to produce ARRAY literals in SQL expressions, e.g.::
 
430
 
 
431
        from sqlalchemy.dialects.postgresql import array
 
432
        from sqlalchemy.dialects import postgresql
 
433
        from sqlalchemy import select, func
 
434
 
 
435
        stmt = select([
 
436
                        array([1,2]) + array([3,4,5])
 
437
                    ])
 
438
 
 
439
        print stmt.compile(dialect=postgresql.dialect())
 
440
 
 
441
    Produces the SQL::
 
442
 
 
443
        SELECT ARRAY[%(param_1)s, %(param_2)s] ||
 
444
            ARRAY[%(param_3)s, %(param_4)s, %(param_5)s]) AS anon_1
 
445
 
 
446
    An instance of :class:`.array` will always have the datatype
 
447
    :class:`.ARRAY`.  The "inner" type of the array is inferred from
 
448
    the values present, unless the ``type_`` keyword argument is passed::
 
449
 
 
450
        array(['foo', 'bar'], type_=CHAR)
 
451
 
 
452
    .. versionadded:: 0.8 Added the :class:`~.postgresql.array` literal type.
 
453
 
 
454
    See also:
 
455
 
 
456
    :class:`.postgresql.ARRAY`
 
457
 
 
458
    """
 
459
    __visit_name__ = 'array'
 
460
 
 
461
    def __init__(self, clauses, **kw):
 
462
        super(array, self).__init__(*clauses, **kw)
 
463
        self.type = ARRAY(self.type)
 
464
 
 
465
    def _bind_param(self, operator, obj):
 
466
        return array(*[
 
467
            expression.BindParameter(None, o, _compared_to_operator=operator,
 
468
                             _compared_to_type=self.type, unique=True)
 
469
            for o in obj
 
470
        ])
 
471
 
 
472
    def self_group(self, against=None):
 
473
        return self
 
474
 
 
475
 
 
476
class ARRAY(sqltypes.Concatenable, sqltypes.TypeEngine):
325
477
    """Postgresql ARRAY type.
326
478
 
327
479
    Represents values as Python lists.
328
480
 
329
 
    The ARRAY type may not be supported on all DBAPIs.
 
481
    An :class:`.ARRAY` type is constructed given the "type"
 
482
    of element::
 
483
 
 
484
        mytable = Table("mytable", metadata,
 
485
                Column("data", ARRAY(Integer))
 
486
            )
 
487
 
 
488
    The above type represents an N-dimensional array,
 
489
    meaning Postgresql will interpret values with any number
 
490
    of dimensions automatically.   To produce an INSERT
 
491
    construct that passes in a 1-dimensional array of integers::
 
492
 
 
493
        connection.execute(
 
494
                mytable.insert(),
 
495
                data=[1,2,3]
 
496
        )
 
497
 
 
498
    The :class:`.ARRAY` type can be constructed given a fixed number
 
499
    of dimensions::
 
500
 
 
501
        mytable = Table("mytable", metadata,
 
502
                Column("data", ARRAY(Integer, dimensions=2))
 
503
            )
 
504
 
 
505
    This has the effect of the :class:`.ARRAY` type
 
506
    specifying that number of bracketed blocks when a :class:`.Table`
 
507
    is used in a CREATE TABLE statement, or when the type is used
 
508
    within a :func:`.expression.cast` construct; it also causes
 
509
    the bind parameter and result set processing of the type
 
510
    to optimize itself to expect exactly that number of dimensions.
 
511
    Note that Postgresql itself still allows N dimensions with such a type.
 
512
 
 
513
    SQL expressions of type :class:`.ARRAY` have support for "index" and
 
514
    "slice" behavior.  The Python ``[]`` operator works normally here, given
 
515
    integer indexes or slices.  Note that Postgresql arrays default
 
516
    to 1-based indexing.  The operator produces binary expression
 
517
    constructs which will produce the appropriate SQL, both for
 
518
    SELECT statements::
 
519
 
 
520
        select([mytable.c.data[5], mytable.c.data[2:7]])
 
521
 
 
522
    as well as UPDATE statements when the :meth:`.Update.values` method
 
523
    is used::
 
524
 
 
525
        mytable.update().values({
 
526
            mytable.c.data[5]: 7,
 
527
            mytable.c.data[2:7]: [1, 2, 3]
 
528
        })
 
529
 
 
530
    :class:`.ARRAY` provides special methods for containment operations,
 
531
    e.g.::
 
532
 
 
533
        mytable.c.data.contains([1, 2])
 
534
 
 
535
    For a full list of special methods see :class:`.ARRAY.Comparator`.
 
536
 
 
537
    .. versionadded:: 0.8 Added support for index and slice operations
 
538
       to the :class:`.ARRAY` type, including support for UPDATE
 
539
       statements, and special array containment operations.
 
540
 
 
541
    The :class:`.ARRAY` type may not be supported on all DBAPIs.
330
542
    It is known to work on psycopg2 and not pg8000.
331
543
 
 
544
    See also:
 
545
 
 
546
    :class:`.postgresql.array` - produce a literal array value.
332
547
 
333
548
    """
334
549
    __visit_name__ = 'ARRAY'
335
550
 
336
 
    def __init__(self, item_type, mutable=False, as_tuple=False):
 
551
    class Comparator(sqltypes.Concatenable.Comparator):
 
552
        """Define comparison operations for :class:`.ARRAY`."""
 
553
 
 
554
        def __getitem__(self, index):
 
555
            if isinstance(index, slice):
 
556
                index = _Slice(index, self)
 
557
                return_type = self.type
 
558
            else:
 
559
                return_type = self.type.item_type
 
560
            return self._binary_operate(self.expr, operators.getitem, index,
 
561
                            result_type=return_type)
 
562
 
 
563
        def any(self, other, operator=operators.eq):
 
564
            """Return ``other operator ANY (array)`` clause.
 
565
 
 
566
            Argument places are switched, because ANY requires array
 
567
            expression to be on the right hand-side.
 
568
 
 
569
            E.g.::
 
570
 
 
571
                from sqlalchemy.sql import operators
 
572
 
 
573
                conn.execute(
 
574
                    select([table.c.data]).where(
 
575
                            table.c.data.any(7, operator=operators.lt)
 
576
                        )
 
577
                )
 
578
 
 
579
            :param other: expression to be compared
 
580
            :param operator: an operator object from the
 
581
             :mod:`sqlalchemy.sql.operators`
 
582
             package, defaults to :func:`.operators.eq`.
 
583
 
 
584
            .. seealso::
 
585
 
 
586
                :class:`.postgresql.Any`
 
587
 
 
588
                :meth:`.postgresql.ARRAY.Comparator.all`
 
589
 
 
590
            """
 
591
            return Any(other, self.expr, operator=operator)
 
592
 
 
593
        def all(self, other, operator=operators.eq):
 
594
            """Return ``other operator ALL (array)`` clause.
 
595
 
 
596
            Argument places are switched, because ALL requires array
 
597
            expression to be on the right hand-side.
 
598
 
 
599
            E.g.::
 
600
 
 
601
                from sqlalchemy.sql import operators
 
602
 
 
603
                conn.execute(
 
604
                    select([table.c.data]).where(
 
605
                            table.c.data.all(7, operator=operators.lt)
 
606
                        )
 
607
                )
 
608
 
 
609
            :param other: expression to be compared
 
610
            :param operator: an operator object from the
 
611
             :mod:`sqlalchemy.sql.operators`
 
612
             package, defaults to :func:`.operators.eq`.
 
613
 
 
614
            .. seealso::
 
615
 
 
616
                :class:`.postgresql.All`
 
617
 
 
618
                :meth:`.postgresql.ARRAY.Comparator.any`
 
619
 
 
620
            """
 
621
            return All(other, self.expr, operator=operator)
 
622
 
 
623
        def contains(self, other, **kwargs):
 
624
            """Boolean expression.  Test if elements are a superset of the
 
625
            elements of the argument array expression.
 
626
            """
 
627
            return self.expr.op('@>')(other)
 
628
 
 
629
        def contained_by(self, other):
 
630
            """Boolean expression.  Test if elements are a proper subset of the
 
631
            elements of the argument array expression.
 
632
            """
 
633
            return self.expr.op('<@')(other)
 
634
 
 
635
        def overlap(self, other):
 
636
            """Boolean expression.  Test if array has elements in common with
 
637
            an argument array expression.
 
638
            """
 
639
            return self.expr.op('&&')(other)
 
640
 
 
641
        def _adapt_expression(self, op, other_comparator):
 
642
            if isinstance(op, operators.custom_op):
 
643
                if op.opstring in ['@>', '<@', '&&']:
 
644
                    return op, sqltypes.Boolean
 
645
            return sqltypes.Concatenable.Comparator.\
 
646
                _adapt_expression(self, op, other_comparator)
 
647
 
 
648
    comparator_factory = Comparator
 
649
 
 
650
    def __init__(self, item_type, as_tuple=False, dimensions=None):
337
651
        """Construct an ARRAY.
338
652
 
339
653
        E.g.::
345
659
        :param item_type: The data type of items of this array. Note that
346
660
          dimensionality is irrelevant here, so multi-dimensional arrays like
347
661
          ``INTEGER[][]``, are constructed as ``ARRAY(Integer)``, not as
348
 
          ``ARRAY(ARRAY(Integer))`` or such. The type mapping figures out on
349
 
          the fly
350
 
 
351
 
        :param mutable=False: Specify whether lists passed to this
352
 
          class should be considered mutable - this enables
353
 
          "mutable types" mode in the ORM.  Be sure to read the
354
 
          notes for :class:`.MutableType` regarding ORM
355
 
          performance implications.
356
 
 
357
 
          .. versionchanged:: 0.7.0
358
 
              Default changed from ``True``\ .
359
 
 
360
 
          .. versionchanged:: 0.7
361
 
              This functionality is now superseded by the
362
 
              ``sqlalchemy.ext.mutable`` extension described in
363
 
              :ref:`mutable_toplevel`.
 
662
          ``ARRAY(ARRAY(Integer))`` or such.
364
663
 
365
664
        :param as_tuple=False: Specify whether return results
366
665
          should be converted to tuples from lists. DBAPIs such
367
666
          as psycopg2 return lists by default. When tuples are
368
 
          returned, the results are hashable. This flag can only
369
 
          be set to ``True`` when ``mutable`` is set to
370
 
          ``False``.
 
667
          returned, the results are hashable.
371
668
 
372
 
          .. versionadded:: 0.6.5
 
669
        :param dimensions: if non-None, the ARRAY will assume a fixed
 
670
         number of dimensions.  This will cause the DDL emitted for this
 
671
         ARRAY to include the exact number of bracket clauses ``[]``,
 
672
         and will also optimize the performance of the type overall.
 
673
         Note that PG arrays are always implicitly "non-dimensioned",
 
674
         meaning they can store any number of dimensions no matter how
 
675
         they were declared.
373
676
 
374
677
        """
375
678
        if isinstance(item_type, ARRAY):
378
681
        if isinstance(item_type, type):
379
682
            item_type = item_type()
380
683
        self.item_type = item_type
381
 
        self.mutable = mutable
382
 
        if mutable and as_tuple:
383
 
            raise exc.ArgumentError(
384
 
                "mutable must be set to False if as_tuple is True."
385
 
            )
386
684
        self.as_tuple = as_tuple
387
 
 
388
 
    def copy_value(self, value):
389
 
        if value is None:
390
 
            return None
391
 
        elif self.mutable:
392
 
            return list(value)
393
 
        else:
394
 
            return value
 
685
        self.dimensions = dimensions
395
686
 
396
687
    def compare_values(self, x, y):
397
688
        return x == y
398
689
 
399
 
    def is_mutable(self):
400
 
        return self.mutable
 
690
    def _proc_array(self, arr, itemproc, dim, collection):
 
691
        if dim is None:
 
692
            arr = list(arr)
 
693
        if dim == 1 or dim is None and (
 
694
                        # this has to be (list, tuple), or at least
 
695
                        # not hasattr('__iter__'), since Py3K strings
 
696
                        # etc. have __iter__
 
697
                        not arr or not isinstance(arr[0], (list, tuple))):
 
698
            if itemproc:
 
699
                return collection(itemproc(x) for x in arr)
 
700
            else:
 
701
                return collection(arr)
 
702
        else:
 
703
            return collection(
 
704
                    self._proc_array(
 
705
                            x, itemproc,
 
706
                            dim - 1 if dim is not None else None,
 
707
                            collection)
 
708
                    for x in arr
 
709
                )
401
710
 
402
711
    def bind_processor(self, dialect):
403
 
        item_proc = self.item_type.dialect_impl(dialect).bind_processor(dialect)
404
 
        if item_proc:
405
 
            def convert_item(item):
406
 
                if isinstance(item, (list, tuple)):
407
 
                    return [convert_item(child) for child in item]
408
 
                else:
409
 
                    return item_proc(item)
410
 
        else:
411
 
            def convert_item(item):
412
 
                if isinstance(item, (list, tuple)):
413
 
                    return [convert_item(child) for child in item]
414
 
                else:
415
 
                    return item
 
712
        item_proc = self.item_type.\
 
713
                        dialect_impl(dialect).\
 
714
                        bind_processor(dialect)
 
715
 
416
716
        def process(value):
417
717
            if value is None:
418
718
                return value
419
 
            return [convert_item(item) for item in value]
 
719
            else:
 
720
                return self._proc_array(
 
721
                            value,
 
722
                            item_proc,
 
723
                            self.dimensions,
 
724
                            list)
420
725
        return process
421
726
 
422
727
    def result_processor(self, dialect, coltype):
423
 
        item_proc = self.item_type.dialect_impl(dialect).result_processor(dialect, coltype)
424
 
        if item_proc:
425
 
            def convert_item(item):
426
 
                if isinstance(item, list):
427
 
                    r = [convert_item(child) for child in item]
428
 
                    if self.as_tuple:
429
 
                        r = tuple(r)
430
 
                    return r
431
 
                else:
432
 
                    return item_proc(item)
433
 
        else:
434
 
            def convert_item(item):
435
 
                if isinstance(item, list):
436
 
                    r = [convert_item(child) for child in item]
437
 
                    if self.as_tuple:
438
 
                        r = tuple(r)
439
 
                    return r
440
 
                else:
441
 
                    return item
 
728
        item_proc = self.item_type.\
 
729
                        dialect_impl(dialect).\
 
730
                        result_processor(dialect, coltype)
 
731
 
442
732
        def process(value):
443
733
            if value is None:
444
734
                return value
445
 
            r = [convert_item(item) for item in value]
446
 
            if self.as_tuple:
447
 
                r = tuple(r)
448
 
            return r
 
735
            else:
 
736
                return self._proc_array(
 
737
                            value,
 
738
                            item_proc,
 
739
                            self.dimensions,
 
740
                            tuple if self.as_tuple else list)
449
741
        return process
 
742
 
450
743
PGArray = ARRAY
451
744
 
 
745
 
452
746
class ENUM(sqltypes.Enum):
453
747
    """Postgresql ENUM type.
454
748
 
585
879
            self.drop(bind=bind, checkfirst=checkfirst)
586
880
 
587
881
colspecs = {
588
 
    sqltypes.Interval:INTERVAL,
589
 
    sqltypes.Enum:ENUM,
 
882
    sqltypes.Interval: INTERVAL,
 
883
    sqltypes.Enum: ENUM,
590
884
}
591
885
 
592
886
ischema_names = {
593
 
    'integer' : INTEGER,
594
 
    'bigint' : BIGINT,
595
 
    'smallint' : SMALLINT,
596
 
    'character varying' : VARCHAR,
597
 
    'character' : CHAR,
598
 
    '"char"' : sqltypes.String,
599
 
    'name' : sqltypes.String,
600
 
    'text' : TEXT,
601
 
    'numeric' : NUMERIC,
602
 
    'float' : FLOAT,
603
 
    'real' : REAL,
 
887
    'integer': INTEGER,
 
888
    'bigint': BIGINT,
 
889
    'smallint': SMALLINT,
 
890
    'character varying': VARCHAR,
 
891
    'character': CHAR,
 
892
    '"char"': sqltypes.String,
 
893
    'name': sqltypes.String,
 
894
    'text': TEXT,
 
895
    'numeric': NUMERIC,
 
896
    'float': FLOAT,
 
897
    'real': REAL,
604
898
    'inet': INET,
605
899
    'cidr': CIDR,
606
900
    'uuid': UUID,
607
901
    'bit': BIT,
608
902
    'bit varying': BIT,
609
903
    'macaddr': MACADDR,
610
 
    'double precision' : DOUBLE_PRECISION,
611
 
    'timestamp' : TIMESTAMP,
612
 
    'timestamp with time zone' : TIMESTAMP,
613
 
    'timestamp without time zone' : TIMESTAMP,
614
 
    'time with time zone' : TIME,
615
 
    'time without time zone' : TIME,
616
 
    'date' : DATE,
 
904
    'double precision': DOUBLE_PRECISION,
 
905
    'timestamp': TIMESTAMP,
 
906
    'timestamp with time zone': TIMESTAMP,
 
907
    'timestamp without time zone': TIMESTAMP,
 
908
    'time with time zone': TIME,
 
909
    'time without time zone': TIME,
 
910
    'date': DATE,
617
911
    'time': TIME,
618
 
    'bytea' : BYTEA,
619
 
    'boolean' : BOOLEAN,
620
 
    'interval':INTERVAL,
621
 
    'interval year to month':INTERVAL,
622
 
    'interval day to second':INTERVAL,
 
912
    'bytea': BYTEA,
 
913
    'boolean': BOOLEAN,
 
914
    'interval': INTERVAL,
 
915
    'interval year to month': INTERVAL,
 
916
    'interval day to second': INTERVAL,
623
917
}
624
918
 
625
919
 
626
 
 
627
920
class PGCompiler(compiler.SQLCompiler):
628
921
 
629
 
    def visit_match_op(self, binary, **kw):
 
922
    def visit_array(self, element, **kw):
 
923
        return "ARRAY[%s]" % self.visit_clauselist(element, **kw)
 
924
 
 
925
    def visit_slice(self, element, **kw):
 
926
        return "%s:%s" % (
 
927
                    self.process(element.start, **kw),
 
928
                    self.process(element.stop, **kw),
 
929
                )
 
930
 
 
931
    def visit_any(self, element, **kw):
 
932
        return "%s%sANY (%s)" % (
 
933
            self.process(element.left, **kw),
 
934
            compiler.OPERATORS[element.operator],
 
935
            self.process(element.right, **kw)
 
936
        )
 
937
 
 
938
    def visit_all(self, element, **kw):
 
939
        return "%s%sALL (%s)" % (
 
940
            self.process(element.left, **kw),
 
941
            compiler.OPERATORS[element.operator],
 
942
            self.process(element.right, **kw)
 
943
        )
 
944
 
 
945
    def visit_getitem_binary(self, binary, operator, **kw):
 
946
        return "%s[%s]" % (
 
947
                self.process(binary.left, **kw),
 
948
                self.process(binary.right, **kw)
 
949
            )
 
950
 
 
951
    def visit_match_op_binary(self, binary, operator, **kw):
630
952
        return "%s @@ to_tsquery(%s)" % (
631
 
                        self.process(binary.left),
632
 
                        self.process(binary.right))
 
953
                        self.process(binary.left, **kw),
 
954
                        self.process(binary.right, **kw))
633
955
 
634
 
    def visit_ilike_op(self, binary, **kw):
 
956
    def visit_ilike_op_binary(self, binary, operator, **kw):
635
957
        escape = binary.modifiers.get("escape", None)
636
958
        return '%s ILIKE %s' % \
637
 
                (self.process(binary.left), self.process(binary.right)) \
 
959
                (self.process(binary.left, **kw),
 
960
                    self.process(binary.right, **kw)) \
638
961
                + (escape and
639
962
                        (' ESCAPE ' + self.render_literal_value(escape, None))
640
963
                        or '')
641
964
 
642
 
    def visit_notilike_op(self, binary, **kw):
 
965
    def visit_notilike_op_binary(self, binary, operator, **kw):
643
966
        escape = binary.modifiers.get("escape", None)
644
967
        return '%s NOT ILIKE %s' % \
645
 
                (self.process(binary.left), self.process(binary.right)) \
 
968
                (self.process(binary.left, **kw),
 
969
                    self.process(binary.right, **kw)) \
646
970
                + (escape and
647
971
                        (' ESCAPE ' + self.render_literal_value(escape, None))
648
972
                        or '')
660
984
    def limit_clause(self, select):
661
985
        text = ""
662
986
        if select._limit is not None:
663
 
            text +=  " \n LIMIT " + self.process(sql.literal(select._limit))
 
987
            text += " \n LIMIT " + self.process(sql.literal(select._limit))
664
988
        if select._offset is not None:
665
989
            if select._limit is None:
666
990
                text += " \n LIMIT ALL"
667
991
            text += " OFFSET " + self.process(sql.literal(select._offset))
668
992
        return text
669
993
 
 
994
    def format_from_hint_text(self, sqltext, table, hint, iscrud):
 
995
        if hint.upper() != 'ONLY':
 
996
            raise exc.CompileError("Unrecognized hint: %r" % hint)
 
997
        return "ONLY " + sqltext
 
998
 
670
999
    def get_select_precolumns(self, select):
671
1000
        if select._distinct is not False:
672
1001
            if select._distinct is True:
674
1003
            elif isinstance(select._distinct, (list, tuple)):
675
1004
                return "DISTINCT ON (" + ', '.join(
676
1005
                    [self.process(col) for col in select._distinct]
677
 
                )+ ") "
 
1006
                ) + ") "
678
1007
            else:
679
1008
                return "DISTINCT ON (" + self.process(select._distinct) + ") "
680
1009
        else:
693
1022
    def returning_clause(self, stmt, returning_cols):
694
1023
 
695
1024
        columns = [
696
 
                self.process(
697
 
                    self.label_select_column(None, c, asfrom=False),
698
 
                    within_columns_clause=True,
699
 
                    result_map=self.result_map)
 
1025
                self._label_select_column(None, c, True, False, {})
700
1026
                for c in expression._select_iterables(returning_cols)
701
1027
            ]
702
1028
 
703
1029
        return 'RETURNING ' + ', '.join(columns)
704
1030
 
705
 
    def visit_extract(self, extract, **kwargs):
706
 
        field = self.extract_map.get(extract.field, extract.field)
707
 
        if extract.expr.type:
708
 
            affinity = extract.expr.type._type_affinity
709
 
        else:
710
 
            affinity = None
711
1031
 
712
 
        casts = {
713
 
                    sqltypes.Date:'date',
714
 
                    sqltypes.DateTime:'timestamp',
715
 
                    sqltypes.Interval:'interval', sqltypes.Time:'time'
716
 
                }
717
 
        cast = casts.get(affinity, None)
718
 
        if isinstance(extract.expr, sql.ColumnElement) and cast is not None:
719
 
            expr = extract.expr.op('::')(sql.literal_column(cast))
 
1032
    def visit_substring_func(self, func, **kw):
 
1033
        s = self.process(func.clauses.clauses[0], **kw)
 
1034
        start = self.process(func.clauses.clauses[1], **kw)
 
1035
        if len(func.clauses.clauses) > 2:
 
1036
            length = self.process(func.clauses.clauses[2], **kw)
 
1037
            return "SUBSTRING(%s FROM %s FOR %s)" % (s, start, length)
720
1038
        else:
721
 
            expr = extract.expr
722
 
        return "EXTRACT(%s FROM %s)" % (
723
 
            field, self.process(expr))
 
1039
            return "SUBSTRING(%s FROM %s)" % (s, start)
724
1040
 
725
1041
class PGDDLCompiler(compiler.DDLCompiler):
726
1042
    def get_column_specification(self, column, **kwargs):
734
1050
                (
735
1051
                    isinstance(column.default, schema.Sequence) and
736
1052
                    column.default.optional
737
 
                )
738
 
            ):
 
1053
                )):
739
1054
            if isinstance(impl_type, sqltypes.BigInteger):
740
1055
                colspec += " BIGSERIAL"
741
1056
            else:
768
1083
    def visit_create_index(self, create):
769
1084
        preparer = self.preparer
770
1085
        index = create.element
 
1086
        self._verify_index_table(index)
771
1087
        text = "CREATE "
772
1088
        if index.unique:
773
1089
            text += "UNIQUE "
774
 
        ops = index.kwargs.get('postgresql_ops', {})
775
1090
        text += "INDEX %s ON %s " % (
776
 
                    preparer.quote(
777
 
                        self._index_identifier(index.name), index.quote),
 
1091
                        self._prepared_index_name(index,
 
1092
                                include_schema=False),
778
1093
                    preparer.format_table(index.table)
779
1094
                )
780
1095
 
782
1097
            using = index.kwargs['postgresql_using']
783
1098
            text += "USING %s " % preparer.quote(using, index.quote)
784
1099
 
 
1100
        ops = index.kwargs.get('postgresql_ops', {})
785
1101
        text += "(%s)" \
786
1102
                % (
787
1103
                    ', '.join([
788
 
                        preparer.format_column(c) +
 
1104
                        self.sql_compiler.process(expr, include_table=False) +
 
1105
 
 
1106
 
789
1107
                        (c.key in ops and (' ' + ops[c.key]) or '')
790
 
                        for c in index.columns])
 
1108
 
 
1109
 
 
1110
                        for expr, c in zip(index.expressions, index.columns)])
791
1111
                    )
792
1112
 
793
 
        if "postgres_where" in index.kwargs:
794
 
            whereclause = index.kwargs['postgres_where']
795
 
            util.warn_deprecated(
796
 
                    "The 'postgres_where' argument has been renamed "
797
 
                    "to 'postgresql_where'.")
798
 
        elif 'postgresql_where' in index.kwargs:
 
1113
        if 'postgresql_where' in index.kwargs:
799
1114
            whereclause = index.kwargs['postgresql_where']
800
1115
        else:
801
1116
            whereclause = None
806
1121
            text += " WHERE " + where_compiled
807
1122
        return text
808
1123
 
 
1124
    def visit_exclude_constraint(self, constraint):
 
1125
        text = ""
 
1126
        if constraint.name is not None:
 
1127
            text += "CONSTRAINT %s " % \
 
1128
                    self.preparer.format_constraint(constraint)
 
1129
        elements = []
 
1130
        for c in constraint.columns:
 
1131
            op = constraint.operators[c.name]
 
1132
            elements.append(self.preparer.quote(c.name, c.quote)+' WITH '+op)
 
1133
        text += "EXCLUDE USING %s (%s)" % (constraint.using, ', '.join(elements))
 
1134
        if constraint.where is not None:
 
1135
            sqltext = sql_util.expression_as_ddl(constraint.where)
 
1136
            text += ' WHERE (%s)' % self.sql_compiler.process(sqltext)
 
1137
        text += self.define_constraint_deferrability(constraint)
 
1138
        return text
 
1139
 
809
1140
 
810
1141
class PGTypeCompiler(compiler.GenericTypeCompiler):
811
1142
    def visit_INET(self, type_):
829
1160
    def visit_BIGINT(self, type_):
830
1161
        return "BIGINT"
831
1162
 
 
1163
    def visit_HSTORE(self, type_):
 
1164
        return "HSTORE"
 
1165
 
 
1166
    def visit_INT4RANGE(self, type_):
 
1167
        return "INT4RANGE"
 
1168
 
 
1169
    def visit_INT8RANGE(self, type_):
 
1170
        return "INT8RANGE"
 
1171
 
 
1172
    def visit_NUMRANGE(self, type_):
 
1173
        return "NUMRANGE"
 
1174
 
 
1175
    def visit_DATERANGE(self, type_):
 
1176
        return "DATERANGE"
 
1177
 
 
1178
    def visit_TSRANGE(self, type_):
 
1179
        return "TSRANGE"
 
1180
 
 
1181
    def visit_TSTZRANGE(self, type_):
 
1182
        return "TSTZRANGE"
 
1183
 
832
1184
    def visit_datetime(self, type_):
833
1185
        return self.visit_TIMESTAMP(type_)
834
1186
 
880
1232
        return "BYTEA"
881
1233
 
882
1234
    def visit_ARRAY(self, type_):
883
 
        return self.process(type_.item_type) + '[]'
 
1235
        return self.process(type_.item_type) + ('[]' * (type_.dimensions
 
1236
                                                if type_.dimensions
 
1237
                                                is not None else 1))
884
1238
 
885
1239
 
886
1240
class PGIdentifierPreparer(compiler.IdentifierPreparer):
902
1256
            name = self.quote_schema(type_.schema, type_.quote) + "." + name
903
1257
        return name
904
1258
 
 
1259
 
905
1260
class PGInspector(reflection.Inspector):
906
1261
 
907
1262
    def __init__(self, conn):
913
1268
        return self.dialect.get_table_oid(self.bind, table_name, schema,
914
1269
                                          info_cache=self.info_cache)
915
1270
 
 
1271
 
916
1272
class CreateEnumType(schema._CreateDropBase):
917
 
  __visit_name__ = "create_enum_type"
 
1273
    __visit_name__ = "create_enum_type"
 
1274
 
918
1275
 
919
1276
class DropEnumType(schema._CreateDropBase):
920
 
  __visit_name__ = "drop_enum_type"
 
1277
    __visit_name__ = "drop_enum_type"
 
1278
 
921
1279
 
922
1280
class PGExecutionContext(default.DefaultExecutionContext):
923
1281
    def fire_sequence(self, seq, type_):
947
1305
                    col = column.name
948
1306
                    tab = tab[0:29 + max(0, (29 - len(col)))]
949
1307
                    col = col[0:29 + max(0, (29 - len(tab)))]
950
 
                    column._postgresql_seq_name = seq_name = "%s_%s_seq" % (tab, col)
 
1308
                    name = "%s_%s_seq" % (tab, col)
 
1309
                    column._postgresql_seq_name = seq_name = name
951
1310
 
952
1311
                sch = column.table.schema
953
1312
                if sch is not None:
961
1320
 
962
1321
        return super(PGExecutionContext, self).get_insert_default(column)
963
1322
 
 
1323
 
964
1324
class PGDialect(default.DefaultDialect):
965
1325
    name = 'postgresql'
966
1326
    supports_alter = True
977
1337
 
978
1338
    supports_default_values = True
979
1339
    supports_empty_insert = False
 
1340
    supports_multivalues_insert = True
980
1341
    default_paramstyle = 'pyformat'
981
1342
    ischema_names = ischema_names
982
1343
    colspecs = colspecs
1082
1443
        return connection.scalar("select current_schema()")
1083
1444
 
1084
1445
    def has_schema(self, connection, schema):
 
1446
        query = "select nspname from pg_namespace where lower(nspname)=:schema"
1085
1447
        cursor = connection.execute(
1086
1448
            sql.text(
1087
 
                "select nspname from pg_namespace where lower(nspname)=:schema",
 
1449
                query,
1088
1450
                bindparams=[
1089
1451
                    sql.bindparam(
1090
1452
                        'schema', unicode(schema.lower()),
1182
1544
 
1183
1545
    def _get_server_version_info(self, connection):
1184
1546
        v = connection.execute("select version()").scalar()
1185
 
        m = re.match('PostgreSQL (\d+)\.(\d+)(?:\.(\d+))?(?:devel)?', v)
 
1547
        m = re.match(
 
1548
            '(?:PostgreSQL|EnterpriseDB) '
 
1549
            '(\d+)\.(\d+)(?:\.(\d+))?(?:\.\d+)?(?:devel)?',
 
1550
            v)
1186
1551
        if not m:
1187
1552
            raise AssertionError(
1188
1553
                    "Could not determine version from string '%s'" % v)
1218
1583
            sql.bindparam('table_name', type_=sqltypes.Unicode),
1219
1584
            sql.bindparam('schema', type_=sqltypes.Unicode)
1220
1585
            ],
1221
 
            typemap={'oid':sqltypes.Integer}
 
1586
            typemap={'oid': sqltypes.Integer}
1222
1587
        )
1223
1588
        c = connection.execute(s, table_name=table_name, schema=schema)
1224
1589
        table_oid = c.scalar()
1257
1622
                "AND '%s' = (select nspname from pg_namespace n "
1258
1623
                "where n.oid = c.relnamespace) " %
1259
1624
                current_schema,
1260
 
                typemap = {'relname':sqltypes.Unicode}
 
1625
                typemap={'relname': sqltypes.Unicode}
1261
1626
            )
1262
1627
        )
1263
1628
        return [row[0] for row in result]
1264
1629
 
1265
 
 
1266
1630
    @reflection.cache
1267
1631
    def get_view_names(self, connection, schema=None, **kw):
1268
1632
        if schema is not None:
1327
1691
        """
1328
1692
        s = sql.text(SQL_COLS,
1329
1693
            bindparams=[sql.bindparam('table_oid', type_=sqltypes.Integer)],
1330
 
            typemap={'attname':sqltypes.Unicode, 'default':sqltypes.Unicode}
 
1694
            typemap={'attname': sqltypes.Unicode, 'default': sqltypes.Unicode}
1331
1695
        )
1332
1696
        c = connection.execute(s, table_oid=table_oid)
1333
1697
        rows = c.fetchall()
1337
1701
        # format columns
1338
1702
        columns = []
1339
1703
        for name, format_type, default, notnull, attnum, table_oid in rows:
1340
 
            ## strip (5) from character varying(5), timestamp(5)
1341
 
            # with time zone, etc
1342
 
            attype = re.sub(r'\([\d,]+\)', '', format_type)
1343
 
 
1344
 
            # strip '[]' from integer[], etc.
1345
 
            attype = re.sub(r'\[\]', '', attype)
1346
 
 
1347
 
            nullable = not notnull
1348
 
            is_array = format_type.endswith('[]')
1349
 
            charlen = re.search('\(([\d,]+)\)', format_type)
1350
 
            if charlen:
1351
 
                charlen = charlen.group(1)
1352
 
            kwargs = {}
1353
 
            args = None
1354
 
 
1355
 
            if attype == 'numeric':
1356
 
                if charlen:
1357
 
                    prec, scale = charlen.split(',')
1358
 
                    args = (int(prec), int(scale))
1359
 
                else:
1360
 
                    args = ()
1361
 
            elif attype == 'double precision':
1362
 
                args = (53, )
1363
 
            elif attype == 'integer':
1364
 
                args = ()
1365
 
            elif attype in ('timestamp with time zone',
1366
 
                            'time with time zone'):
1367
 
                kwargs['timezone'] = True
1368
 
                if charlen:
1369
 
                    kwargs['precision'] = int(charlen)
1370
 
                args = ()
1371
 
            elif attype in ('timestamp without time zone',
1372
 
                            'time without time zone', 'time'):
1373
 
                kwargs['timezone'] = False
1374
 
                if charlen:
1375
 
                    kwargs['precision'] = int(charlen)
1376
 
                args = ()
1377
 
            elif attype == 'bit varying':
1378
 
                kwargs['varying'] = True
1379
 
                if charlen:
1380
 
                    args = (int(charlen),)
1381
 
                else:
1382
 
                    args = ()
1383
 
            elif attype in ('interval','interval year to month',
1384
 
                                'interval day to second'):
1385
 
                if charlen:
1386
 
                    kwargs['precision'] = int(charlen)
1387
 
                args = ()
1388
 
            elif charlen:
1389
 
                args = (int(charlen),)
1390
 
            else:
1391
 
                args = ()
1392
 
 
1393
 
            while True:
1394
 
                if attype in self.ischema_names:
1395
 
                    coltype = self.ischema_names[attype]
1396
 
                    break
1397
 
                elif attype in enums:
1398
 
                    enum = enums[attype]
1399
 
                    coltype = ENUM
1400
 
                    if "." in attype:
1401
 
                        kwargs['schema'], kwargs['name'] = attype.split('.')
1402
 
                    else:
1403
 
                        kwargs['name'] = attype
1404
 
                    args = tuple(enum['labels'])
1405
 
                    break
1406
 
                elif attype in domains:
1407
 
                    domain = domains[attype]
1408
 
                    attype = domain['attype']
1409
 
                    # A table can't override whether the domain is nullable.
1410
 
                    nullable = domain['nullable']
1411
 
                    if domain['default'] and not default:
1412
 
                        # It can, however, override the default
1413
 
                        # value, but can't set it to null.
1414
 
                        default = domain['default']
1415
 
                    continue
1416
 
                else:
1417
 
                    coltype = None
1418
 
                    break
1419
 
 
1420
 
            if coltype:
1421
 
                coltype = coltype(*args, **kwargs)
1422
 
                if is_array:
1423
 
                    coltype = ARRAY(coltype)
1424
 
            else:
1425
 
                util.warn("Did not recognize type '%s' of column '%s'" %
1426
 
                          (attype, name))
1427
 
                coltype = sqltypes.NULLTYPE
1428
 
            # adjust the default value
1429
 
            autoincrement = False
1430
 
            if default is not None:
1431
 
                match = re.search(r"""(nextval\(')([^']+)('.*$)""", default)
1432
 
                if match is not None:
1433
 
                    autoincrement = True
1434
 
                    # the default is related to a Sequence
1435
 
                    sch = schema
1436
 
                    if '.' not in match.group(2) and sch is not None:
1437
 
                        # unconditionally quote the schema name.  this could
1438
 
                        # later be enhanced to obey quoting rules /
1439
 
                        # "quote schema"
1440
 
                        default = match.group(1) + \
1441
 
                                    ('"%s"' % sch) + '.' + \
1442
 
                                    match.group(2) + match.group(3)
1443
 
 
1444
 
            column_info = dict(name=name, type=coltype, nullable=nullable,
1445
 
                               default=default, autoincrement=autoincrement)
 
1704
            column_info = self._get_column_info(
 
1705
                name, format_type, default, notnull, domains, enums, schema)
1446
1706
            columns.append(column_info)
1447
1707
        return columns
1448
1708
 
 
1709
    def _get_column_info(self, name, format_type, default,
 
1710
                         notnull, domains, enums, schema):
 
1711
        ## strip (*) from character varying(5), timestamp(5)
 
1712
        # with time zone, geometry(POLYGON), etc.
 
1713
        attype = re.sub(r'\(.*\)', '', format_type)
 
1714
 
 
1715
        # strip '[]' from integer[], etc.
 
1716
        attype = re.sub(r'\[\]', '', attype)
 
1717
 
 
1718
        nullable = not notnull
 
1719
        is_array = format_type.endswith('[]')
 
1720
        charlen = re.search('\(([\d,]+)\)', format_type)
 
1721
        if charlen:
 
1722
            charlen = charlen.group(1)
 
1723
        args = re.search('\((.*)\)', format_type)
 
1724
        if args and args.group(1):
 
1725
            args = tuple(re.split('\s*,\s*', args.group(1)))
 
1726
        else:
 
1727
            args = ()
 
1728
        kwargs = {}
 
1729
 
 
1730
        if attype == 'numeric':
 
1731
            if charlen:
 
1732
                prec, scale = charlen.split(',')
 
1733
                args = (int(prec), int(scale))
 
1734
            else:
 
1735
                args = ()
 
1736
        elif attype == 'double precision':
 
1737
            args = (53, )
 
1738
        elif attype == 'integer':
 
1739
            args = ()
 
1740
        elif attype in ('timestamp with time zone',
 
1741
                        'time with time zone'):
 
1742
            kwargs['timezone'] = True
 
1743
            if charlen:
 
1744
                kwargs['precision'] = int(charlen)
 
1745
            args = ()
 
1746
        elif attype in ('timestamp without time zone',
 
1747
                        'time without time zone', 'time'):
 
1748
            kwargs['timezone'] = False
 
1749
            if charlen:
 
1750
                kwargs['precision'] = int(charlen)
 
1751
            args = ()
 
1752
        elif attype == 'bit varying':
 
1753
            kwargs['varying'] = True
 
1754
            if charlen:
 
1755
                args = (int(charlen),)
 
1756
            else:
 
1757
                args = ()
 
1758
        elif attype in ('interval', 'interval year to month',
 
1759
                            'interval day to second'):
 
1760
            if charlen:
 
1761
                kwargs['precision'] = int(charlen)
 
1762
            args = ()
 
1763
        elif charlen:
 
1764
            args = (int(charlen),)
 
1765
 
 
1766
        while True:
 
1767
            if attype in self.ischema_names:
 
1768
                coltype = self.ischema_names[attype]
 
1769
                break
 
1770
            elif attype in enums:
 
1771
                enum = enums[attype]
 
1772
                coltype = ENUM
 
1773
                if "." in attype:
 
1774
                    kwargs['schema'], kwargs['name'] = attype.split('.')
 
1775
                else:
 
1776
                    kwargs['name'] = attype
 
1777
                args = tuple(enum['labels'])
 
1778
                break
 
1779
            elif attype in domains:
 
1780
                domain = domains[attype]
 
1781
                attype = domain['attype']
 
1782
                # A table can't override whether the domain is nullable.
 
1783
                nullable = domain['nullable']
 
1784
                if domain['default'] and not default:
 
1785
                    # It can, however, override the default
 
1786
                    # value, but can't set it to null.
 
1787
                    default = domain['default']
 
1788
                continue
 
1789
            else:
 
1790
                coltype = None
 
1791
                break
 
1792
 
 
1793
        if coltype:
 
1794
            coltype = coltype(*args, **kwargs)
 
1795
            if is_array:
 
1796
                coltype = ARRAY(coltype)
 
1797
        else:
 
1798
            util.warn("Did not recognize type '%s' of column '%s'" %
 
1799
                      (attype, name))
 
1800
            coltype = sqltypes.NULLTYPE
 
1801
        # adjust the default value
 
1802
        autoincrement = False
 
1803
        if default is not None:
 
1804
            match = re.search(r"""(nextval\(')([^']+)('.*$)""", default)
 
1805
            if match is not None:
 
1806
                autoincrement = True
 
1807
                # the default is related to a Sequence
 
1808
                sch = schema
 
1809
                if '.' not in match.group(2) and sch is not None:
 
1810
                    # unconditionally quote the schema name.  this could
 
1811
                    # later be enhanced to obey quoting rules /
 
1812
                    # "quote schema"
 
1813
                    default = match.group(1) + \
 
1814
                                ('"%s"' % sch) + '.' + \
 
1815
                                match.group(2) + match.group(3)
 
1816
 
 
1817
        column_info = dict(name=name, type=coltype, nullable=nullable,
 
1818
                           default=default, autoincrement=autoincrement)
 
1819
        return column_info
 
1820
 
1449
1821
    @reflection.cache
1450
 
    def get_primary_keys(self, connection, table_name, schema=None, **kw):
 
1822
    def get_pk_constraint(self, connection, table_name, schema=None, **kw):
1451
1823
        table_oid = self.get_table_oid(connection, table_name, schema,
1452
1824
                                       info_cache=kw.get('info_cache'))
1453
1825
 
1479
1851
            """
1480
1852
        t = sql.text(PK_SQL, typemap={'attname': sqltypes.Unicode})
1481
1853
        c = connection.execute(t, table_oid=table_oid)
1482
 
        primary_keys = [r[0] for r in c.fetchall()]
1483
 
        return primary_keys
1484
 
 
1485
 
    @reflection.cache
1486
 
    def get_pk_constraint(self, connection, table_name, schema=None, **kw):
1487
 
        cols = self.get_primary_keys(connection, table_name,
1488
 
                                            schema=schema, **kw)
1489
 
 
1490
 
        table_oid = self.get_table_oid(connection, table_name, schema,
1491
 
                                       info_cache=kw.get('info_cache'))
 
1854
        cols = [r[0] for r in c.fetchall()]
1492
1855
 
1493
1856
        PK_CONS_SQL = """
1494
1857
        SELECT conname
1496
1859
           WHERE r.conrelid = :table_oid AND r.contype = 'p'
1497
1860
           ORDER BY 1
1498
1861
        """
1499
 
        t = sql.text(PK_CONS_SQL, typemap={'conname':sqltypes.Unicode})
 
1862
        t = sql.text(PK_CONS_SQL, typemap={'conname': sqltypes.Unicode})
1500
1863
        c = connection.execute(t, table_oid=table_oid)
1501
1864
        name = c.scalar()
1502
 
        return {
1503
 
            'constrained_columns':cols,
1504
 
            'name':name
1505
 
        }
 
1865
 
 
1866
        return {'constrained_columns': cols, 'name': name}
1506
1867
 
1507
1868
    @reflection.cache
1508
1869
    def get_foreign_keys(self, connection, table_name, schema=None, **kw):
1526
1887
        """
1527
1888
 
1528
1889
        t = sql.text(FK_SQL, typemap={
1529
 
                                'conname':sqltypes.Unicode,
1530
 
                                'condef':sqltypes.Unicode})
 
1890
                                'conname': sqltypes.Unicode,
 
1891
                                'condef': sqltypes.Unicode})
1531
1892
        c = connection.execute(t, table=table_oid)
1532
1893
        fkeys = []
1533
1894
        for conname, condef, conschema in c.fetchall():
1553
1914
            referred_columns = [preparer._unquote_identifier(x)
1554
1915
                        for x in re.split(r'\s*,\s', referred_columns)]
1555
1916
            fkey_d = {
1556
 
                'name' : conname,
1557
 
                'constrained_columns' : constrained_columns,
1558
 
                'referred_schema' : referred_schema,
1559
 
                'referred_table' : referred_table,
1560
 
                'referred_columns' : referred_columns
 
1917
                'name': conname,
 
1918
                'constrained_columns': constrained_columns,
 
1919
                'referred_schema': referred_schema,
 
1920
                'referred_table': referred_table,
 
1921
                'referred_columns': referred_columns
1561
1922
            }
1562
1923
            fkeys.append(fkey_d)
1563
1924
        return fkeys
1571
1932
          SELECT
1572
1933
              i.relname as relname,
1573
1934
              ix.indisunique, ix.indexprs, ix.indpred,
1574
 
              a.attname
 
1935
              a.attname, a.attnum, ix.indkey
1575
1936
          FROM
1576
1937
              pg_class t
1577
1938
                    join pg_index ix on t.oid = ix.indrelid
1588
1949
              i.relname
1589
1950
        """
1590
1951
 
1591
 
        t = sql.text(IDX_SQL, typemap={'attname':sqltypes.Unicode})
 
1952
        t = sql.text(IDX_SQL, typemap={'attname': sqltypes.Unicode})
1592
1953
        c = connection.execute(t, table_oid=table_oid)
1593
1954
 
1594
 
        index_names = {}
1595
 
        indexes = []
 
1955
        indexes = defaultdict(lambda: defaultdict(dict))
 
1956
 
1596
1957
        sv_idx_name = None
1597
1958
        for row in c.fetchall():
1598
 
            idx_name, unique, expr, prd, col = row
 
1959
            idx_name, unique, expr, prd, col, col_num, idx_key = row
 
1960
 
1599
1961
            if expr:
1600
1962
                if idx_name != sv_idx_name:
1601
1963
                    util.warn(
1604
1966
                      % idx_name)
1605
1967
                sv_idx_name = idx_name
1606
1968
                continue
 
1969
 
1607
1970
            if prd and not idx_name == sv_idx_name:
1608
1971
                util.warn(
1609
1972
                   "Predicate of partial index %s ignored during reflection"
1610
1973
                   % idx_name)
1611
1974
                sv_idx_name = idx_name
1612
 
            if idx_name in index_names:
1613
 
                index_d = index_names[idx_name]
1614
 
            else:
1615
 
                index_d = {'column_names':[]}
1616
 
                indexes.append(index_d)
1617
 
                index_names[idx_name] = index_d
1618
 
            index_d['name'] = idx_name
 
1975
 
 
1976
            index = indexes[idx_name]
1619
1977
            if col is not None:
1620
 
                index_d['column_names'].append(col)
1621
 
            index_d['unique'] = unique
1622
 
        return indexes
 
1978
                index['cols'][col_num] = col
 
1979
            index['key'] = [int(k.strip()) for k in idx_key.split()]
 
1980
            index['unique'] = unique
 
1981
 
 
1982
        return [
 
1983
            {'name': name,
 
1984
             'unique': idx['unique'],
 
1985
             'column_names': [idx['cols'][i] for i in idx['key']]}
 
1986
            for name, idx in indexes.items()
 
1987
        ]
1623
1988
 
1624
1989
    def _load_enums(self, connection):
1625
1990
        if not self.supports_native_enum:
1641
2006
        """
1642
2007
 
1643
2008
        s = sql.text(SQL_ENUMS, typemap={
1644
 
                                'attname':sqltypes.Unicode,
1645
 
                                'label':sqltypes.Unicode})
 
2009
                                'attname': sqltypes.Unicode,
 
2010
                                'label': sqltypes.Unicode})
1646
2011
        c = connection.execute(s)
1647
2012
 
1648
2013
        enums = {}
1679
2044
            WHERE t.typtype = 'd'
1680
2045
        """
1681
2046
 
1682
 
        s = sql.text(SQL_DOMAINS, typemap={'attname':sqltypes.Unicode})
 
2047
        s = sql.text(SQL_DOMAINS, typemap={'attname': sqltypes.Unicode})
1683
2048
        c = connection.execute(s)
1684
2049
 
1685
2050
        domains = {}
1696
2061
                name = "%s.%s" % (domain['schema'], domain['name'])
1697
2062
 
1698
2063
            domains[name] = {
1699
 
                    'attype':attype,
 
2064
                    'attype': attype,
1700
2065
                    'nullable': domain['nullable'],
1701
2066
                    'default': domain['default']
1702
2067
                }
1703
2068
 
1704
2069
        return domains
1705