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

« back to all changes in this revision

Viewing changes to lib/sqlalchemy/dialects/oracle/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
# oracle/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 Oracle database.
8
 
 
9
 
Oracle version 8 through current (11g at the time of this writing) are supported.
10
 
 
11
 
For information on connecting via specific drivers, see the documentation
12
 
for that driver.
 
7
"""
 
8
.. dialect:: oracle
 
9
    :name: Oracle
 
10
 
 
11
    Oracle version 8 through current (11g at the time of this writing) are supported.
13
12
 
14
13
Connect Arguments
15
14
-----------------
140
139
 
141
140
"""
142
141
 
143
 
import random, re
 
142
import re
144
143
 
145
 
from sqlalchemy import schema as sa_schema
146
 
from sqlalchemy import util, sql, log
 
144
from sqlalchemy import util, sql
147
145
from sqlalchemy.engine import default, base, reflection
148
146
from sqlalchemy.sql import compiler, visitors, expression
149
147
from sqlalchemy.sql import operators as sql_operators, functions as sql_functions
165
163
NO_ARG_FNS = set('UID CURRENT_DATE SYSDATE USER '
166
164
                'CURRENT_TIME CURRENT_TIMESTAMP'.split())
167
165
 
 
166
 
168
167
class RAW(sqltypes._Binary):
169
168
    __visit_name__ = 'RAW'
170
169
OracleRaw = RAW
171
170
 
 
171
 
172
172
class NCLOB(sqltypes.Text):
173
173
    __visit_name__ = 'NCLOB'
174
174
 
 
175
 
175
176
class VARCHAR2(VARCHAR):
176
177
    __visit_name__ = 'VARCHAR2'
177
178
 
178
179
NVARCHAR2 = NVARCHAR
179
180
 
 
181
 
180
182
class NUMBER(sqltypes.Numeric, sqltypes.Integer):
181
183
    __visit_name__ = 'NUMBER'
182
184
 
202
204
 
203
205
class DOUBLE_PRECISION(sqltypes.Numeric):
204
206
    __visit_name__ = 'DOUBLE_PRECISION'
 
207
 
205
208
    def __init__(self, precision=None, scale=None, asdecimal=None):
206
209
        if asdecimal is None:
207
210
            asdecimal = False
208
211
 
209
212
        super(DOUBLE_PRECISION, self).__init__(precision=precision, scale=scale, asdecimal=asdecimal)
210
213
 
 
214
 
211
215
class BFILE(sqltypes.LargeBinary):
212
216
    __visit_name__ = 'BFILE'
213
217
 
 
218
 
214
219
class LONG(sqltypes.Text):
215
220
    __visit_name__ = 'LONG'
216
221
 
 
222
 
217
223
class INTERVAL(sqltypes.TypeEngine):
218
224
    __visit_name__ = 'INTERVAL'
219
225
 
244
250
    def _type_affinity(self):
245
251
        return sqltypes.Interval
246
252
 
 
253
 
247
254
class ROWID(sqltypes.TypeEngine):
248
255
    """Oracle ROWID type.
249
256
 
253
260
    __visit_name__ = 'ROWID'
254
261
 
255
262
 
256
 
 
257
263
class _OracleBoolean(sqltypes.Boolean):
258
264
    def get_dbapi_type(self, dbapi):
259
265
        return dbapi.NUMBER
260
266
 
261
267
colspecs = {
262
 
    sqltypes.Boolean : _OracleBoolean,
263
 
    sqltypes.Interval : INTERVAL,
 
268
    sqltypes.Boolean: _OracleBoolean,
 
269
    sqltypes.Interval: INTERVAL,
264
270
}
265
271
 
266
272
ischema_names = {
267
 
    'VARCHAR2' : VARCHAR,
268
 
    'NVARCHAR2' : NVARCHAR,
269
 
    'CHAR' : CHAR,
270
 
    'DATE' : DATE,
271
 
    'NUMBER' : NUMBER,
272
 
    'BLOB' : BLOB,
273
 
    'BFILE' : BFILE,
274
 
    'CLOB' : CLOB,
275
 
    'NCLOB' : NCLOB,
276
 
    'TIMESTAMP' : TIMESTAMP,
277
 
    'TIMESTAMP WITH TIME ZONE' : TIMESTAMP,
278
 
    'INTERVAL DAY TO SECOND' : INTERVAL,
279
 
    'RAW' : RAW,
280
 
    'FLOAT' : FLOAT,
281
 
    'DOUBLE PRECISION' : DOUBLE_PRECISION,
282
 
    'LONG' : LONG,
 
273
    'VARCHAR2': VARCHAR,
 
274
    'NVARCHAR2': NVARCHAR,
 
275
    'CHAR': CHAR,
 
276
    'DATE': DATE,
 
277
    'NUMBER': NUMBER,
 
278
    'BLOB': BLOB,
 
279
    'BFILE': BFILE,
 
280
    'CLOB': CLOB,
 
281
    'NCLOB': NCLOB,
 
282
    'TIMESTAMP': TIMESTAMP,
 
283
    'TIMESTAMP WITH TIME ZONE': TIMESTAMP,
 
284
    'INTERVAL DAY TO SECOND': INTERVAL,
 
285
    'RAW': RAW,
 
286
    'FLOAT': FLOAT,
 
287
    'DOUBLE PRECISION': DOUBLE_PRECISION,
 
288
    'LONG': LONG,
283
289
}
284
290
 
285
291
 
336
342
        if precision is None:
337
343
            return name
338
344
        elif scale is None:
339
 
            return "%(name)s(%(precision)s)" % {'name':name,'precision': precision}
 
345
            n = "%(name)s(%(precision)s)"
 
346
            return n % {'name': name, 'precision': precision}
340
347
        else:
341
 
            return "%(name)s(%(precision)s, %(scale)s)" % {'name':name,'precision': precision, 'scale' : scale}
 
348
            n = "%(name)s(%(precision)s, %(scale)s)"
 
349
            return n % {'name': name, 'precision': precision, 'scale': scale}
342
350
 
343
351
    def visit_string(self, type_):
344
352
        return self.visit_VARCHAR2(type_)
355
363
 
356
364
    def _visit_varchar(self, type_, n, num):
357
365
        if not n and self.dialect._supports_char_length:
358
 
            return "VARCHAR%(two)s(%(length)s CHAR)" % {
359
 
                                                    'length' : type_.length,
360
 
                                                    'two':num}
 
366
            varchar = "VARCHAR%(two)s(%(length)s CHAR)"
 
367
            return varchar % {'length': type_.length, 'two': num}
361
368
        else:
362
 
            return "%(n)sVARCHAR%(two)s(%(length)s)" % {'length' : type_.length,
363
 
                                                        'two':num, 'n':n}
 
369
            varchar = "%(n)sVARCHAR%(two)s(%(length)s)"
 
370
            return varchar % {'length': type_.length, 'two': num, 'n': n}
364
371
 
365
372
    def visit_text(self, type_):
366
373
        return self.visit_CLOB(type_)
382
389
 
383
390
    def visit_RAW(self, type_):
384
391
        if type_.length:
385
 
            return "RAW(%(length)s)" % {'length' : type_.length}
 
392
            return "RAW(%(length)s)" % {'length': type_.length}
386
393
        else:
387
394
            return "RAW"
388
395
 
389
396
    def visit_ROWID(self, type_):
390
397
        return "ROWID"
391
398
 
 
399
 
392
400
class OracleCompiler(compiler.SQLCompiler):
393
401
    """Oracle compiler modifies the lexical structure of Select
394
402
    statements to work under non-ANSI configured Oracle databases, if
398
406
    compound_keywords = util.update_copy(
399
407
        compiler.SQLCompiler.compound_keywords,
400
408
        {
401
 
        expression.CompoundSelect.EXCEPT : 'MINUS'
 
409
        expression.CompoundSelect.EXCEPT: 'MINUS'
402
410
        }
403
411
    )
404
412
 
407
415
        self._quoted_bind_names = {}
408
416
        super(OracleCompiler, self).__init__(*args, **kwargs)
409
417
 
410
 
    def visit_mod(self, binary, **kw):
411
 
        return "mod(%s, %s)" % (self.process(binary.left), self.process(binary.right))
 
418
    def visit_mod_binary(self, binary, operator, **kw):
 
419
        return "mod(%s, %s)" % (self.process(binary.left, **kw),
 
420
                                self.process(binary.right, **kw))
412
421
 
413
422
    def visit_now_func(self, fn, **kw):
414
423
        return "CURRENT_TIMESTAMP"
416
425
    def visit_char_length_func(self, fn, **kw):
417
426
        return "LENGTH" + self.function_argspec(fn, **kw)
418
427
 
419
 
    def visit_match_op(self, binary, **kw):
420
 
        return "CONTAINS (%s, %s)" % (self.process(binary.left), self.process(binary.right))
 
428
    def visit_match_op_binary(self, binary, operator, **kw):
 
429
        return "CONTAINS (%s, %s)" % (self.process(binary.left),
 
430
                                        self.process(binary.right))
 
431
 
 
432
    def visit_true(self, expr, **kw):
 
433
        return '1'
 
434
 
 
435
    def visit_false(self, expr, **kw):
 
436
        return '0'
421
437
 
422
438
    def get_select_hint_text(self, byfroms):
423
439
        return " ".join(
459
475
                        elif binary.right.table is join.right:
460
476
                            binary.right = _OuterJoinColumn(binary.right)
461
477
                clauses.append(visitors.cloned_traverse(join.onclause, {},
462
 
                                {'binary':visit_binary}))
 
478
                                {'binary': visit_binary}))
463
479
            else:
464
480
                clauses.append(join.onclause)
465
481
 
499
515
 
500
516
    def returning_clause(self, stmt, returning_cols):
501
517
 
502
 
        def create_out_param(col, i):
503
 
            bindparam = sql.outparam("ret_%d" % i, type_=col.type)
504
 
            self.binds[bindparam.key] = bindparam
505
 
            return self.bindparam_string(self._truncate_bindparam(bindparam))
506
 
 
507
 
        columnlist = list(expression._select_iterables(returning_cols))
508
 
 
509
 
        # within_columns_clause =False so that labels (foo AS bar) don't render
510
 
        columns = [self.process(c, within_columns_clause=False, result_map=self.result_map) for c in columnlist]
511
 
 
512
 
        binds = [create_out_param(c, i) for i, c in enumerate(columnlist)]
513
 
 
514
 
        return 'RETURNING ' + ', '.join(columns) +  " INTO " + ", ".join(binds)
 
518
        columns = []
 
519
        binds = []
 
520
        for i, column in enumerate(expression._select_iterables(returning_cols)):
 
521
            if column.type._has_column_expression:
 
522
                col_expr = column.type.column_expression(column)
 
523
            else:
 
524
                col_expr = column
 
525
            outparam = sql.outparam("ret_%d" % i, type_=column.type)
 
526
            self.binds[outparam.key] = outparam
 
527
            binds.append(self.bindparam_string(self._truncate_bindparam(outparam)))
 
528
            columns.append(self.process(col_expr, within_columns_clause=False))
 
529
            self.result_map[outparam.key] = (
 
530
                outparam.key,
 
531
                (column, getattr(column, 'name', None),
 
532
                                        getattr(column, 'key', None)),
 
533
                column.type
 
534
            )
 
535
 
 
536
        return 'RETURNING ' + ', '.join(columns) + " INTO " + ", ".join(binds)
515
537
 
516
538
    def _TODO_visit_compound_select(self, select):
517
539
        """Need to determine how to get ``LIMIT``/``OFFSET`` into a ``UNION`` for Oracle."""
524
546
 
525
547
        if not getattr(select, '_oracle_visit', None):
526
548
            if not self.dialect.use_ansi:
527
 
                if self.stack and 'from' in self.stack[-1]:
528
 
                    existingfroms = self.stack[-1]['from']
529
 
                else:
530
 
                    existingfroms = None
531
 
 
532
 
                froms = select._get_display_froms(existingfroms)
 
549
                froms = self._display_froms_for_select(
 
550
                                    select, kwargs.get('asfrom', False))
533
551
                whereclause = self._get_nonansi_join_whereclause(froms)
534
552
                if whereclause is not None:
535
553
                    select = select.where(whereclause)
579
597
                    limitselect._is_wrapper = True
580
598
 
581
599
                    offsetselect = sql.select(
582
 
                             [c for c in limitselect.c if c.key!='ora_rn'])
 
600
                             [c for c in limitselect.c if c.key != 'ora_rn'])
583
601
                    offsetselect._oracle_visit = True
584
602
                    offsetselect._is_wrapper = True
585
603
 
587
605
                    if not self.dialect.use_binds_for_limits:
588
606
                        offset_value = sql.literal_column("%d" % offset_value)
589
607
                    offsetselect.append_whereclause(
590
 
                             sql.literal_column("ora_rn")>offset_value)
 
608
                             sql.literal_column("ora_rn") > offset_value)
591
609
 
592
610
                    offsetselect.for_update = select.for_update
593
611
                    select = offsetselect
606
624
        else:
607
625
            return super(OracleCompiler, self).for_update_clause(select)
608
626
 
 
627
 
609
628
class OracleDDLCompiler(compiler.DDLCompiler):
610
629
 
611
630
    def define_constraint_cascades(self, constraint):
623
642
 
624
643
        return text
625
644
 
 
645
    def visit_create_index(self, create, **kw):
 
646
        return super(OracleDDLCompiler, self).\
 
647
                    visit_create_index(create, include_schema=True)
 
648
 
 
649
 
626
650
class OracleIdentifierPreparer(compiler.IdentifierPreparer):
627
651
 
628
652
    reserved_words = set([x.lower() for x in RESERVED_WORDS])
647
671
                    self.dialect.identifier_preparer.format_sequence(seq) +
648
672
                    ".nextval FROM DUAL", type_)
649
673
 
 
674
 
650
675
class OracleDialect(default.DefaultDialect):
651
676
    name = 'oracle'
652
677
    supports_alter = True
808
833
 
809
834
        if resolve_synonyms:
810
835
            actual_name, owner, dblink, synonym = self._resolve_synonym(
811
 
                                                         connection,
812
 
                                                         desired_owner=self.denormalize_name(schema),
813
 
                                                         desired_synonym=self.denormalize_name(table_name)
814
 
                                                   )
 
836
                        connection,
 
837
                         desired_owner=self.denormalize_name(schema),
 
838
                         desired_synonym=self.denormalize_name(table_name)
 
839
                       )
815
840
        else:
816
841
            actual_name, owner, dblink, synonym = None, None, None, None
817
842
        if not actual_name:
818
843
            actual_name = self.denormalize_name(table_name)
819
 
        if not dblink:
820
 
            dblink = ''
821
 
        if not owner:
 
844
 
 
845
        if dblink:
 
846
            # using user_db_links here since all_db_links appears
 
847
            # to have more restricted permissions.
 
848
            # http://docs.oracle.com/cd/B28359_01/server.111/b28310/ds_admin005.htm
 
849
            # will need to hear from more users if we are doing
 
850
            # the right thing here.  See [ticket:2619]
 
851
            owner = connection.scalar(
 
852
                            sql.text("SELECT username FROM user_db_links "
 
853
                                    "WHERE db_link=:link"), link=dblink)
 
854
            dblink = "@" + dblink
 
855
        elif not owner:
822
856
            owner = self.denormalize_name(schema or self.default_schema_name)
823
 
        return (actual_name, owner, dblink, synonym)
 
857
 
 
858
        return (actual_name, owner, dblink or '', synonym)
824
859
 
825
860
    @reflection.cache
826
861
    def get_schema_names(self, connection, **kw):
843
878
        cursor = connection.execute(s, owner=schema)
844
879
        return [self.normalize_name(row[0]) for row in cursor]
845
880
 
846
 
 
847
881
    @reflection.cache
848
882
    def get_view_names(self, connection, schema=None, **kw):
849
883
        schema = self.denormalize_name(schema or self.default_schema_name)
877
911
        else:
878
912
            char_length_col = 'data_length'
879
913
 
880
 
        c = connection.execute(sql.text(
881
 
                "SELECT column_name, data_type, %(char_length_col)s, data_precision, data_scale, "
882
 
                "nullable, data_default FROM ALL_TAB_COLUMNS%(dblink)s "
883
 
                "WHERE table_name = :table_name AND owner = :owner "
884
 
                "ORDER BY column_id" % {'dblink': dblink, 'char_length_col':char_length_col}),
885
 
                               table_name=table_name, owner=schema)
 
914
        params = {"table_name": table_name}
 
915
        text = "SELECT column_name, data_type, %(char_length_col)s, "\
 
916
                "data_precision, data_scale, "\
 
917
                "nullable, data_default FROM ALL_TAB_COLUMNS%(dblink)s "\
 
918
                "WHERE table_name = :table_name"
 
919
        if schema is not None:
 
920
            params['owner'] = schema
 
921
            text += " AND owner = :owner "
 
922
        text += " ORDER BY column_id"
 
923
        text = text % {'dblink': dblink, 'char_length_col': char_length_col}
 
924
 
 
925
        c = connection.execute(sql.text(text), **params)
886
926
 
887
927
        for row in c:
888
928
            (colname, orig_colname, coltype, length, precision, scale, nullable, default) = \
889
 
                (self.normalize_name(row[0]), row[0], row[1], row[2], row[3], row[4], row[5]=='Y', row[6])
 
929
                (self.normalize_name(row[0]), row[0], row[1], row[2], row[3], row[4], row[5] == 'Y', row[6])
890
930
 
891
 
            if coltype == 'NUMBER' :
 
931
            if coltype == 'NUMBER':
892
932
                coltype = NUMBER(precision, scale)
893
933
            elif coltype in ('VARCHAR2', 'NVARCHAR2', 'CHAR'):
894
934
                coltype = self.ischema_names.get(coltype)(length)
908
948
                'type': coltype,
909
949
                'nullable': nullable,
910
950
                'default': default,
911
 
                'autoincrement':default is None
 
951
                'autoincrement': default is None
912
952
            }
913
953
            if orig_colname.lower() == orig_colname:
914
954
                cdict['quote'] = True
920
960
    def get_indexes(self, connection, table_name, schema=None,
921
961
                    resolve_synonyms=False, dblink='', **kw):
922
962
 
923
 
 
924
963
        info_cache = kw.get('info_cache')
925
964
        (table_name, schema, dblink, synonym) = \
926
965
            self._prepare_reflection_args(connection, table_name, schema,
927
966
                                          resolve_synonyms, dblink,
928
967
                                          info_cache=info_cache)
929
968
        indexes = []
930
 
        q = sql.text("""
931
 
        SELECT a.index_name, a.column_name, b.uniqueness
932
 
        FROM ALL_IND_COLUMNS%(dblink)s a,
933
 
        ALL_INDEXES%(dblink)s b
934
 
        WHERE
935
 
            a.index_name = b.index_name
936
 
            AND a.table_owner = b.table_owner
937
 
            AND a.table_name = b.table_name
938
 
 
939
 
        AND a.table_name = :table_name
940
 
        AND a.table_owner = :schema
941
 
        ORDER BY a.index_name, a.column_position""" % {'dblink': dblink})
942
 
        rp = connection.execute(q, table_name=self.denormalize_name(table_name),
943
 
                                schema=self.denormalize_name(schema))
 
969
 
 
970
        params = {'table_name': table_name}
 
971
        text = \
 
972
            "SELECT a.index_name, a.column_name, b.uniqueness "\
 
973
            "\nFROM ALL_IND_COLUMNS%(dblink)s a, "\
 
974
            "\nALL_INDEXES%(dblink)s b "\
 
975
            "\nWHERE "\
 
976
            "\na.index_name = b.index_name "\
 
977
            "\nAND a.table_owner = b.table_owner "\
 
978
            "\nAND a.table_name = b.table_name "\
 
979
            "\nAND a.table_name = :table_name "
 
980
 
 
981
        if schema is not None:
 
982
            params['schema'] = schema
 
983
            text += "AND a.table_owner = :schema "
 
984
 
 
985
        text += "ORDER BY a.index_name, a.column_position"
 
986
 
 
987
        text = text % {'dblink': dblink}
 
988
 
 
989
        q = sql.text(text)
 
990
        rp = connection.execute(q, **params)
944
991
        indexes = []
945
992
        last_index_name = None
946
 
        pkeys = self.get_primary_keys(connection, table_name, schema,
947
 
                                      resolve_synonyms=resolve_synonyms,
948
 
                                      dblink=dblink,
949
 
                                      info_cache=kw.get('info_cache'))
 
993
        pk_constraint = self.get_pk_constraint(
 
994
            connection, table_name, schema, resolve_synonyms=resolve_synonyms,
 
995
            dblink=dblink, info_cache=kw.get('info_cache'))
 
996
        pkeys = pk_constraint['constrained_columns']
950
997
        uniqueness = dict(NONUNIQUE=False, UNIQUE=True)
951
998
 
952
999
        oracle_sys_col = re.compile(r'SYS_NC\d+\$', re.IGNORECASE)
982
1029
    def _get_constraint_data(self, connection, table_name, schema=None,
983
1030
                            dblink='', **kw):
984
1031
 
985
 
        rp = connection.execute(
986
 
            sql.text("""SELECT
987
 
             ac.constraint_name,
988
 
             ac.constraint_type,
989
 
             loc.column_name AS local_column,
990
 
             rem.table_name AS remote_table,
991
 
             rem.column_name AS remote_column,
992
 
             rem.owner AS remote_owner,
993
 
             loc.position as loc_pos,
994
 
             rem.position as rem_pos
995
 
           FROM all_constraints%(dblink)s ac,
996
 
             all_cons_columns%(dblink)s loc,
997
 
             all_cons_columns%(dblink)s rem
998
 
           WHERE ac.table_name = :table_name
999
 
           AND ac.constraint_type IN ('R','P')
1000
 
           AND ac.owner = :owner
1001
 
           AND ac.owner = loc.owner
1002
 
           AND ac.constraint_name = loc.constraint_name
1003
 
           AND ac.r_owner = rem.owner(+)
1004
 
           AND ac.r_constraint_name = rem.constraint_name(+)
1005
 
           AND (rem.position IS NULL or loc.position=rem.position)
1006
 
           ORDER BY ac.constraint_name, loc.position""" % {'dblink': dblink}),
1007
 
            table_name=table_name, owner=schema)
 
1032
        params = {'table_name': table_name}
 
1033
 
 
1034
        text = \
 
1035
            "SELECT"\
 
1036
            "\nac.constraint_name,"\
 
1037
            "\nac.constraint_type,"\
 
1038
            "\nloc.column_name AS local_column,"\
 
1039
            "\nrem.table_name AS remote_table,"\
 
1040
            "\nrem.column_name AS remote_column,"\
 
1041
            "\nrem.owner AS remote_owner,"\
 
1042
            "\nloc.position as loc_pos,"\
 
1043
            "\nrem.position as rem_pos"\
 
1044
            "\nFROM all_constraints%(dblink)s ac,"\
 
1045
            "\nall_cons_columns%(dblink)s loc,"\
 
1046
            "\nall_cons_columns%(dblink)s rem"\
 
1047
            "\nWHERE ac.table_name = :table_name"\
 
1048
            "\nAND ac.constraint_type IN ('R','P')"
 
1049
 
 
1050
        if schema is not None:
 
1051
            params['owner'] = schema
 
1052
            text += "\nAND ac.owner = :owner"
 
1053
 
 
1054
        text += \
 
1055
            "\nAND ac.owner = loc.owner"\
 
1056
            "\nAND ac.constraint_name = loc.constraint_name"\
 
1057
            "\nAND ac.r_owner = rem.owner(+)"\
 
1058
            "\nAND ac.r_constraint_name = rem.constraint_name(+)"\
 
1059
            "\nAND (rem.position IS NULL or loc.position=rem.position)"\
 
1060
            "\nORDER BY ac.constraint_name, loc.position"
 
1061
 
 
1062
        text = text % {'dblink': dblink}
 
1063
        rp = connection.execute(sql.text(text), **params)
1008
1064
        constraint_data = rp.fetchall()
1009
1065
        return constraint_data
1010
1066
 
1011
 
    def get_primary_keys(self, connection, table_name, schema=None, **kw):
1012
 
        """
1013
 
 
1014
 
        kw arguments can be:
1015
 
 
1016
 
            oracle_resolve_synonyms
1017
 
 
1018
 
            dblink
1019
 
 
1020
 
        """
1021
 
        return self._get_primary_keys(connection, table_name, schema, **kw)[0]
1022
 
 
1023
1067
    @reflection.cache
1024
 
    def _get_primary_keys(self, connection, table_name, schema=None, **kw):
 
1068
    def get_pk_constraint(self, connection, table_name, schema=None, **kw):
1025
1069
        resolve_synonyms = kw.get('oracle_resolve_synonyms', False)
1026
1070
        dblink = kw.get('dblink', '')
1027
1071
        info_cache = kw.get('info_cache')
1037
1081
                                        info_cache=kw.get('info_cache'))
1038
1082
 
1039
1083
        for row in constraint_data:
1040
 
            #print "ROW:" , row
1041
1084
            (cons_name, cons_type, local_column, remote_table, remote_column, remote_owner) = \
1042
1085
                row[0:2] + tuple([self.normalize_name(x) for x in row[2:6]])
1043
1086
            if cons_type == 'P':
1044
1087
                if constraint_name is None:
1045
1088
                    constraint_name = self.normalize_name(cons_name)
1046
1089
                pkeys.append(local_column)
1047
 
        return pkeys, constraint_name
1048
 
 
1049
 
    def get_pk_constraint(self, connection, table_name, schema=None, **kw):
1050
 
        cols, name = self._get_primary_keys(connection, table_name, schema=schema, **kw)
1051
 
 
1052
 
        return {
1053
 
            'constrained_columns':cols,
1054
 
            'name':name
1055
 
        }
 
1090
        return {'constrained_columns': pkeys, 'name': constraint_name}
1056
1091
 
1057
1092
    @reflection.cache
1058
1093
    def get_foreign_keys(self, connection, table_name, schema=None, **kw):
1066
1101
 
1067
1102
        """
1068
1103
 
1069
 
        requested_schema = schema # to check later on
 
1104
        requested_schema = schema  # to check later on
1070
1105
        resolve_synonyms = kw.get('oracle_resolve_synonyms', False)
1071
1106
        dblink = kw.get('dblink', '')
1072
1107
        info_cache = kw.get('info_cache')
1082
1117
 
1083
1118
        def fkey_rec():
1084
1119
            return {
1085
 
                'name' : None,
1086
 
                'constrained_columns' : [],
1087
 
                'referred_schema' : None,
1088
 
                'referred_table' : None,
1089
 
                'referred_columns' : []
 
1120
                'name': None,
 
1121
                'constrained_columns': [],
 
1122
                'referred_schema': None,
 
1123
                'referred_table': None,
 
1124
                'referred_columns': []
1090
1125
            }
1091
1126
 
1092
1127
        fkeys = util.defaultdict(fkey_rec)
1101
1136
                    util.warn(
1102
1137
                        ("Got 'None' querying 'table_name' from "
1103
1138
                         "all_cons_columns%(dblink)s - does the user have "
1104
 
                         "proper rights to the table?") % {'dblink':dblink})
 
1139
                         "proper rights to the table?") % {'dblink': dblink})
1105
1140
                    continue
1106
1141
 
1107
1142
                rec = fkeys[cons_name]
1138
1173
            self._prepare_reflection_args(connection, view_name, schema,
1139
1174
                                          resolve_synonyms, dblink,
1140
1175
                                          info_cache=info_cache)
1141
 
        s = sql.text("""
1142
 
        SELECT text FROM all_views
1143
 
        WHERE owner = :schema
1144
 
        AND view_name = :view_name
1145
 
        """)
1146
 
        rp = connection.execute(s,
1147
 
                                view_name=view_name, schema=schema).scalar()
 
1176
 
 
1177
        params = {'view_name': view_name}
 
1178
        text = "SELECT text FROM all_views WHERE view_name=:view_name"
 
1179
 
 
1180
        if schema is not None:
 
1181
            text += " AND owner = :schema"
 
1182
            params['schema'] = schema
 
1183
 
 
1184
        rp = connection.execute(sql.text(text), **params).scalar()
1148
1185
        if rp:
1149
1186
            return rp.decode(self.encoding)
1150
1187
        else:
1151
1188
            return None
1152
1189
 
1153
1190
 
1154
 
 
1155
1191
class _OuterJoinColumn(sql.ClauseElement):
1156
1192
    __visit_name__ = 'outer_join_column'
1157
1193
 
1158
1194
    def __init__(self, column):
1159
1195
        self.column = column
1160
 
 
1161
 
 
1162