1# sql/compiler.py 
    2# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors 
    3# <see AUTHORS file> 
    4# 
    5# This module is part of SQLAlchemy and is released under 
    6# the MIT License: http://www.opensource.org/licenses/mit-license.php 
    7 
    8"""Base SQL and DDL compiler implementations. 
    9 
    10Classes provided include: 
    11 
    12:class:`.compiler.SQLCompiler` - renders SQL 
    13strings 
    14 
    15:class:`.compiler.DDLCompiler` - renders DDL 
    16(data definition language) strings 
    17 
    18:class:`.compiler.GenericTypeCompiler` - renders 
    19type specification strings. 
    20 
    21To generate user-defined SQL strings, see 
    22:doc:`/ext/compiler`. 
    23 
    24""" 
    25 
    26import contextlib 
    27import itertools 
    28import re 
    29 
    30from . import crud 
    31from . import elements 
    32from . import functions 
    33from . import operators 
    34from . import schema 
    35from . import selectable 
    36from . import sqltypes 
    37from . import visitors 
    38from .. import exc 
    39from .. import util 
    40 
    41 
    42RESERVED_WORDS = set( 
    43    [ 
    44        "all", 
    45        "analyse", 
    46        "analyze", 
    47        "and", 
    48        "any", 
    49        "array", 
    50        "as", 
    51        "asc", 
    52        "asymmetric", 
    53        "authorization", 
    54        "between", 
    55        "binary", 
    56        "both", 
    57        "case", 
    58        "cast", 
    59        "check", 
    60        "collate", 
    61        "column", 
    62        "constraint", 
    63        "create", 
    64        "cross", 
    65        "current_date", 
    66        "current_role", 
    67        "current_time", 
    68        "current_timestamp", 
    69        "current_user", 
    70        "default", 
    71        "deferrable", 
    72        "desc", 
    73        "distinct", 
    74        "do", 
    75        "else", 
    76        "end", 
    77        "except", 
    78        "false", 
    79        "for", 
    80        "foreign", 
    81        "freeze", 
    82        "from", 
    83        "full", 
    84        "grant", 
    85        "group", 
    86        "having", 
    87        "ilike", 
    88        "in", 
    89        "initially", 
    90        "inner", 
    91        "intersect", 
    92        "into", 
    93        "is", 
    94        "isnull", 
    95        "join", 
    96        "leading", 
    97        "left", 
    98        "like", 
    99        "limit", 
    100        "localtime", 
    101        "localtimestamp", 
    102        "natural", 
    103        "new", 
    104        "not", 
    105        "notnull", 
    106        "null", 
    107        "off", 
    108        "offset", 
    109        "old", 
    110        "on", 
    111        "only", 
    112        "or", 
    113        "order", 
    114        "outer", 
    115        "overlaps", 
    116        "placing", 
    117        "primary", 
    118        "references", 
    119        "right", 
    120        "select", 
    121        "session_user", 
    122        "set", 
    123        "similar", 
    124        "some", 
    125        "symmetric", 
    126        "table", 
    127        "then", 
    128        "to", 
    129        "trailing", 
    130        "true", 
    131        "union", 
    132        "unique", 
    133        "user", 
    134        "using", 
    135        "verbose", 
    136        "when", 
    137        "where", 
    138    ] 
    139) 
    140 
    141LEGAL_CHARACTERS = re.compile(r"^[A-Z0-9_$]+$", re.I) 
    142LEGAL_CHARACTERS_PLUS_SPACE = re.compile(r"^[A-Z0-9_ $]+$", re.I) 
    143ILLEGAL_INITIAL_CHARACTERS = {str(x) for x in range(0, 10)}.union(["$"]) 
    144 
    145FK_ON_DELETE = re.compile( 
    146    r"^(?:RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT)$", re.I 
    147) 
    148FK_ON_UPDATE = re.compile( 
    149    r"^(?:RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT)$", re.I 
    150) 
    151FK_INITIALLY = re.compile(r"^(?:DEFERRED|IMMEDIATE)$", re.I) 
    152BIND_PARAMS = re.compile(r"(?<![:\w\$\x5c]):([\w\$]+)(?![:\w\$])", re.UNICODE) 
    153BIND_PARAMS_ESC = re.compile(r"\x5c(:[\w\$]*)(?![:\w\$])", re.UNICODE) 
    154 
    155BIND_TEMPLATES = { 
    156    "pyformat": "%%(%(name)s)s", 
    157    "qmark": "?", 
    158    "format": "%%s", 
    159    "numeric": ":[_POSITION]", 
    160    "named": ":%(name)s", 
    161} 
    162 
    163 
    164OPERATORS = { 
    165    # binary 
    166    operators.and_: " AND ", 
    167    operators.or_: " OR ", 
    168    operators.add: " + ", 
    169    operators.mul: " * ", 
    170    operators.sub: " - ", 
    171    operators.div: " / ", 
    172    operators.mod: " % ", 
    173    operators.truediv: " / ", 
    174    operators.neg: "-", 
    175    operators.lt: " < ", 
    176    operators.le: " <= ", 
    177    operators.ne: " != ", 
    178    operators.gt: " > ", 
    179    operators.ge: " >= ", 
    180    operators.eq: " = ", 
    181    operators.is_distinct_from: " IS DISTINCT FROM ", 
    182    operators.isnot_distinct_from: " IS NOT DISTINCT FROM ", 
    183    operators.concat_op: " || ", 
    184    operators.match_op: " MATCH ", 
    185    operators.notmatch_op: " NOT MATCH ", 
    186    operators.in_op: " IN ", 
    187    operators.notin_op: " NOT IN ", 
    188    operators.comma_op: ", ", 
    189    operators.from_: " FROM ", 
    190    operators.as_: " AS ", 
    191    operators.is_: " IS ", 
    192    operators.isnot: " IS NOT ", 
    193    operators.collate: " COLLATE ", 
    194    # unary 
    195    operators.exists: "EXISTS ", 
    196    operators.distinct_op: "DISTINCT ", 
    197    operators.inv: "NOT ", 
    198    operators.any_op: "ANY ", 
    199    operators.all_op: "ALL ", 
    200    # modifiers 
    201    operators.desc_op: " DESC", 
    202    operators.asc_op: " ASC", 
    203    operators.nullsfirst_op: " NULLS FIRST", 
    204    operators.nullslast_op: " NULLS LAST", 
    205} 
    206 
    207FUNCTIONS = { 
    208    functions.coalesce: "coalesce", 
    209    functions.current_date: "CURRENT_DATE", 
    210    functions.current_time: "CURRENT_TIME", 
    211    functions.current_timestamp: "CURRENT_TIMESTAMP", 
    212    functions.current_user: "CURRENT_USER", 
    213    functions.localtime: "LOCALTIME", 
    214    functions.localtimestamp: "LOCALTIMESTAMP", 
    215    functions.random: "random", 
    216    functions.sysdate: "sysdate", 
    217    functions.session_user: "SESSION_USER", 
    218    functions.user: "USER", 
    219    functions.cube: "CUBE", 
    220    functions.rollup: "ROLLUP", 
    221    functions.grouping_sets: "GROUPING SETS", 
    222} 
    223 
    224EXTRACT_MAP = { 
    225    "month": "month", 
    226    "day": "day", 
    227    "year": "year", 
    228    "second": "second", 
    229    "hour": "hour", 
    230    "doy": "doy", 
    231    "minute": "minute", 
    232    "quarter": "quarter", 
    233    "dow": "dow", 
    234    "week": "week", 
    235    "epoch": "epoch", 
    236    "milliseconds": "milliseconds", 
    237    "microseconds": "microseconds", 
    238    "timezone_hour": "timezone_hour", 
    239    "timezone_minute": "timezone_minute", 
    240} 
    241 
    242COMPOUND_KEYWORDS = { 
    243    selectable.CompoundSelect.UNION: "UNION", 
    244    selectable.CompoundSelect.UNION_ALL: "UNION ALL", 
    245    selectable.CompoundSelect.EXCEPT: "EXCEPT", 
    246    selectable.CompoundSelect.EXCEPT_ALL: "EXCEPT ALL", 
    247    selectable.CompoundSelect.INTERSECT: "INTERSECT", 
    248    selectable.CompoundSelect.INTERSECT_ALL: "INTERSECT ALL", 
    249} 
    250 
    251 
    252class Compiled(object): 
    253 
    254    """Represent a compiled SQL or DDL expression. 
    255 
    256    The ``__str__`` method of the ``Compiled`` object should produce 
    257    the actual text of the statement.  ``Compiled`` objects are 
    258    specific to their underlying database dialect, and also may 
    259    or may not be specific to the columns referenced within a 
    260    particular set of bind parameters.  In no case should the 
    261    ``Compiled`` object be dependent on the actual values of those 
    262    bind parameters, even though it may reference those values as 
    263    defaults. 
    264    """ 
    265 
    266    _cached_metadata = None 
    267 
    268    schema_translate_map = None 
    269 
    270    execution_options = util.immutabledict() 
    271    """ 
    272    Execution options propagated from the statement.   In some cases, 
    273    sub-elements of the statement can modify these. 
    274    """ 
    275 
    276    def __init__( 
    277        self, 
    278        dialect, 
    279        statement, 
    280        bind=None, 
    281        schema_translate_map=None, 
    282        compile_kwargs=util.immutabledict(), 
    283    ): 
    284        """Construct a new :class:`.Compiled` object. 
    285 
    286        :param dialect: :class:`.Dialect` to compile against. 
    287 
    288        :param statement: :class:`_expression.ClauseElement` to be compiled. 
    289 
    290        :param bind: Optional Engine or Connection to compile this 
    291          statement against. 
    292 
    293        :param schema_translate_map: dictionary of schema names to be 
    294         translated when forming the resultant SQL 
    295 
    296         .. versionadded:: 1.1 
    297 
    298         .. seealso:: 
    299 
    300            :ref:`schema_translating` 
    301 
    302        :param compile_kwargs: additional kwargs that will be 
    303         passed to the initial call to :meth:`.Compiled.process`. 
    304 
    305 
    306        """ 
    307 
    308        self.dialect = dialect 
    309        self.bind = bind 
    310        self.preparer = self.dialect.identifier_preparer 
    311        self.schema_translate_map = schema_translate_map 
    312        if schema_translate_map: 
    313            self.preparer = self.preparer._with_schema_translate( 
    314                schema_translate_map 
    315            ) 
    316 
    317        if statement is not None: 
    318            self.statement = statement 
    319            self.can_execute = statement.supports_execution 
    320            if self.can_execute: 
    321                self.execution_options = statement._execution_options 
    322            self.string = self.process(self.statement, **compile_kwargs) 
    323 
    324    @util.deprecated( 
    325        "0.7", 
    326        "The :meth:`.Compiled.compile` method is deprecated and will be " 
    327        "removed in a future release.   The :class:`.Compiled` object " 
    328        "now runs its compilation within the constructor, and this method " 
    329        "does nothing.", 
    330    ) 
    331    def compile(self): 
    332        """Produce the internal string representation of this element.""" 
    333        pass 
    334 
    335    def _execute_on_connection(self, connection, multiparams, params): 
    336        if self.can_execute: 
    337            return connection._execute_compiled(self, multiparams, params) 
    338        else: 
    339            raise exc.ObjectNotExecutableError(self.statement) 
    340 
    341    @property 
    342    def sql_compiler(self): 
    343        """Return a Compiled that is capable of processing SQL expressions. 
    344 
    345        If this compiler is one, it would likely just return 'self'. 
    346 
    347        """ 
    348 
    349        raise NotImplementedError() 
    350 
    351    def process(self, obj, **kwargs): 
    352        return obj._compiler_dispatch(self, **kwargs) 
    353 
    354    def __str__(self): 
    355        """Return the string text of the generated SQL or DDL.""" 
    356 
    357        return self.string or "" 
    358 
    359    def construct_params(self, params=None): 
    360        """Return the bind params for this compiled object. 
    361 
    362        :param params: a dict of string/object pairs whose values will 
    363                       override bind values compiled in to the 
    364                       statement. 
    365        """ 
    366 
    367        raise NotImplementedError() 
    368 
    369    @property 
    370    def params(self): 
    371        """Return the bind params for this compiled object.""" 
    372        return self.construct_params() 
    373 
    374    def execute(self, *multiparams, **params): 
    375        """Execute this compiled object.""" 
    376 
    377        e = self.bind 
    378        if e is None: 
    379            raise exc.UnboundExecutionError( 
    380                "This Compiled object is not bound to any Engine " 
    381                "or Connection.", 
    382                code="2afi", 
    383            ) 
    384        return e._execute_compiled(self, multiparams, params) 
    385 
    386    def scalar(self, *multiparams, **params): 
    387        """Execute this compiled object and return the result's 
    388        scalar value.""" 
    389 
    390        return self.execute(*multiparams, **params).scalar() 
    391 
    392 
    393class TypeCompiler(util.with_metaclass(util.EnsureKWArgType, object)): 
    394    """Produces DDL specification for TypeEngine objects.""" 
    395 
    396    ensure_kwarg = r"visit_\w+" 
    397 
    398    def __init__(self, dialect): 
    399        self.dialect = dialect 
    400 
    401    def process(self, type_, **kw): 
    402        return type_._compiler_dispatch(self, **kw) 
    403 
    404 
    405class _CompileLabel(visitors.Visitable): 
    406 
    407    """lightweight label object which acts as an expression.Label.""" 
    408 
    409    __visit_name__ = "label" 
    410    __slots__ = "element", "name" 
    411 
    412    def __init__(self, col, name, alt_names=()): 
    413        self.element = col 
    414        self.name = name 
    415        self._alt_names = (col,) + alt_names 
    416 
    417    @property 
    418    def proxy_set(self): 
    419        return self.element.proxy_set 
    420 
    421    @property 
    422    def type(self): 
    423        return self.element.type 
    424 
    425    def self_group(self, **kw): 
    426        return self 
    427 
    428 
    429class prefix_anon_map(dict): 
    430    """A map that creates new keys for missing key access. 
    431    Considers keys of the form "<ident> <name>" to produce 
    432    new symbols "<name>_<index>", where "index" is an incrementing integer 
    433    corresponding to <name>. 
    434    Inlines the approach taken by :class:`sqlalchemy.util.PopulateDict` which 
    435    is otherwise usually used for this type of operation. 
    436    """ 
    437 
    438    def __missing__(self, key): 
    439        (ident, derived) = key.split(" ", 1) 
    440        anonymous_counter = self.get(derived, 1) 
    441        self[derived] = anonymous_counter + 1 
    442        value = derived + "_" + str(anonymous_counter) 
    443        self[key] = value 
    444        return value 
    445 
    446 
    447class SQLCompiler(Compiled): 
    448    """Default implementation of :class:`.Compiled`. 
    449 
    450    Compiles :class:`_expression.ClauseElement` objects into SQL strings. 
    451 
    452    """ 
    453 
    454    extract_map = EXTRACT_MAP 
    455 
    456    compound_keywords = COMPOUND_KEYWORDS 
    457 
    458    isdelete = isinsert = isupdate = False 
    459    """class-level defaults which can be set at the instance 
    460    level to define if this Compiled instance represents 
    461    INSERT/UPDATE/DELETE 
    462    """ 
    463 
    464    isplaintext = False 
    465 
    466    returning = None 
    467    """holds the "returning" collection of columns if 
    468    the statement is CRUD and defines returning columns 
    469    either implicitly or explicitly 
    470    """ 
    471 
    472    returning_precedes_values = False 
    473    """set to True classwide to generate RETURNING 
    474    clauses before the VALUES or WHERE clause (i.e. MSSQL) 
    475    """ 
    476 
    477    render_table_with_column_in_update_from = False 
    478    """set to True classwide to indicate the SET clause 
    479    in a multi-table UPDATE statement should qualify 
    480    columns with the table name (i.e. MySQL only) 
    481    """ 
    482 
    483    contains_expanding_parameters = False 
    484    """True if we've encountered bindparam(..., expanding=True). 
    485 
    486    These need to be converted before execution time against the 
    487    string statement. 
    488 
    489    """ 
    490 
    491    ansi_bind_rules = False 
    492    """SQL 92 doesn't allow bind parameters to be used 
    493    in the columns clause of a SELECT, nor does it allow 
    494    ambiguous expressions like "? = ?".  A compiler 
    495    subclass can set this flag to False if the target 
    496    driver/DB enforces this 
    497    """ 
    498 
    499    _textual_ordered_columns = False 
    500    """tell the result object that the column names as rendered are important, 
    501    but they are also "ordered" vs. what is in the compiled object here. 
    502    """ 
    503 
    504    _ordered_columns = True 
    505    """ 
    506    if False, means we can't be sure the list of entries 
    507    in _result_columns is actually the rendered order.  Usually 
    508    True unless using an unordered TextAsFrom. 
    509    """ 
    510 
    511    _numeric_binds = False 
    512    """ 
    513    True if paramstyle is "numeric".  This paramstyle is trickier than 
    514    all the others. 
    515 
    516    """ 
    517 
    518    insert_single_values_expr = None 
    519    """When an INSERT is compiled with a single set of parameters inside 
    520    a VALUES expression, the string is assigned here, where it can be 
    521    used for insert batching schemes to rewrite the VALUES expression. 
    522 
    523    .. versionadded:: 1.3.8 
    524 
    525    """ 
    526 
    527    insert_prefetch = update_prefetch = () 
    528 
    529    def __init__( 
    530        self, dialect, statement, column_keys=None, inline=False, **kwargs 
    531    ): 
    532        """Construct a new :class:`.SQLCompiler` object. 
    533 
    534        :param dialect: :class:`.Dialect` to be used 
    535 
    536        :param statement: :class:`_expression.ClauseElement` to be compiled 
    537 
    538        :param column_keys:  a list of column names to be compiled into an 
    539         INSERT or UPDATE statement. 
    540 
    541        :param inline: whether to generate INSERT statements as "inline", e.g. 
    542         not formatted to return any generated defaults 
    543 
    544        :param kwargs: additional keyword arguments to be consumed by the 
    545         superclass. 
    546 
    547        """ 
    548        self.column_keys = column_keys 
    549 
    550        # compile INSERT/UPDATE defaults/sequences inlined (no pre- 
    551        # execute) 
    552        self.inline = inline or getattr(statement, "inline", False) 
    553 
    554        # a dictionary of bind parameter keys to BindParameter 
    555        # instances. 
    556        self.binds = {} 
    557 
    558        # a dictionary of BindParameter instances to "compiled" names 
    559        # that are actually present in the generated SQL 
    560        self.bind_names = util.column_dict() 
    561 
    562        # stack which keeps track of nested SELECT statements 
    563        self.stack = [] 
    564 
    565        # relates label names in the final SQL to a tuple of local 
    566        # column/label name, ColumnElement object (if any) and 
    567        # TypeEngine. ResultProxy uses this for type processing and 
    568        # column targeting 
    569        self._result_columns = [] 
    570 
    571        # true if the paramstyle is positional 
    572        self.positional = dialect.positional 
    573        if self.positional: 
    574            self.positiontup = [] 
    575            self._numeric_binds = dialect.paramstyle == "numeric" 
    576        self.bindtemplate = BIND_TEMPLATES[dialect.paramstyle] 
    577 
    578        self.ctes = None 
    579 
    580        self.label_length = ( 
    581            dialect.label_length or dialect.max_identifier_length 
    582        ) 
    583 
    584        # a map which tracks "anonymous" identifiers that are created on 
    585        # the fly here 
    586        self.anon_map = prefix_anon_map() 
    587 
    588        # a map which tracks "truncated" names based on 
    589        # dialect.label_length or dialect.max_identifier_length 
    590        self.truncated_names = {} 
    591 
    592        Compiled.__init__(self, dialect, statement, **kwargs) 
    593 
    594        if ( 
    595            self.isinsert or self.isupdate or self.isdelete 
    596        ) and statement._returning: 
    597            self.returning = statement._returning 
    598 
    599        if self.positional and self._numeric_binds: 
    600            self._apply_numbered_params() 
    601 
    602    @property 
    603    def current_executable(self): 
    604        """Return the current 'executable' that is being compiled. 
    605 
    606        This is currently the :class:`_sql.Select`, :class:`_sql.Insert`, 
    607        :class:`_sql.Update`, :class:`_sql.Delete`, 
    608        :class:`_sql.CompoundSelect` object that is being compiled. 
    609        Specifically it's assigned to the ``self.stack`` list of elements. 
    610 
    611        When a statement like the above is being compiled, it normally 
    612        is also assigned to the ``.statement`` attribute of the 
    613        :class:`_sql.Compiler` object.   However, all SQL constructs are 
    614        ultimately nestable, and this attribute should never be consulted 
    615        by a ``visit_`` method, as it is not guaranteed to be assigned 
    616        nor guaranteed to correspond to the current statement being compiled. 
    617 
    618        .. versionadded:: 1.3.21 
    619 
    620            For compatibility with previous versions, use the following 
    621            recipe:: 
    622 
    623                statement = getattr(self, "current_executable", False) 
    624                if statement is False: 
    625                    statement = self.stack[-1]["selectable"] 
    626 
    627            For versions 1.4 and above, ensure only .current_executable 
    628            is used; the format of "self.stack" may change. 
    629 
    630 
    631        """ 
    632        try: 
    633            return self.stack[-1]["selectable"] 
    634        except IndexError as ie: 
    635            util.raise_( 
    636                IndexError("Compiler does not have a stack entry"), 
    637                replace_context=ie, 
    638            ) 
    639 
    640    @property 
    641    def prefetch(self): 
    642        return list(self.insert_prefetch + self.update_prefetch) 
    643 
    644    @util.memoized_instancemethod 
    645    def _init_cte_state(self): 
    646        """Initialize collections related to CTEs only if 
    647        a CTE is located, to save on the overhead of 
    648        these collections otherwise. 
    649 
    650        """ 
    651        # collect CTEs to tack on top of a SELECT 
    652        self.ctes = util.OrderedDict() 
    653        self.ctes_by_name = {} 
    654        self.ctes_recursive = False 
    655        if self.positional: 
    656            self.cte_positional = {} 
    657 
    658    @contextlib.contextmanager 
    659    def _nested_result(self): 
    660        """special API to support the use case of 'nested result sets'""" 
    661        result_columns, ordered_columns = ( 
    662            self._result_columns, 
    663            self._ordered_columns, 
    664        ) 
    665        self._result_columns, self._ordered_columns = [], False 
    666 
    667        try: 
    668            if self.stack: 
    669                entry = self.stack[-1] 
    670                entry["need_result_map_for_nested"] = True 
    671            else: 
    672                entry = None 
    673            yield self._result_columns, self._ordered_columns 
    674        finally: 
    675            if entry: 
    676                entry.pop("need_result_map_for_nested") 
    677            self._result_columns, self._ordered_columns = ( 
    678                result_columns, 
    679                ordered_columns, 
    680            ) 
    681 
    682    def _apply_numbered_params(self): 
    683        poscount = itertools.count(1) 
    684        self.string = re.sub( 
    685            r"\[_POSITION\]", lambda m: str(util.next(poscount)), self.string 
    686        ) 
    687 
    688    @util.memoized_property 
    689    def _bind_processors(self): 
    690        return dict( 
    691            (key, value) 
    692            for key, value in ( 
    693                ( 
    694                    self.bind_names[bindparam], 
    695                    bindparam.type._cached_bind_processor(self.dialect), 
    696                ) 
    697                for bindparam in self.bind_names 
    698            ) 
    699            if value is not None 
    700        ) 
    701 
    702    def is_subquery(self): 
    703        return len(self.stack) > 1 
    704 
    705    @property 
    706    def sql_compiler(self): 
    707        return self 
    708 
    709    def construct_params(self, params=None, _group_number=None, _check=True): 
    710        """return a dictionary of bind parameter keys and values""" 
    711 
    712        if params: 
    713            pd = {} 
    714            for bindparam in self.bind_names: 
    715                name = self.bind_names[bindparam] 
    716                if bindparam.key in params: 
    717                    pd[name] = params[bindparam.key] 
    718                elif name in params: 
    719                    pd[name] = params[name] 
    720 
    721                elif _check and bindparam.required: 
    722                    if _group_number: 
    723                        raise exc.InvalidRequestError( 
    724                            "A value is required for bind parameter %r, " 
    725                            "in parameter group %d" 
    726                            % (bindparam.key, _group_number), 
    727                            code="cd3x", 
    728                        ) 
    729                    else: 
    730                        raise exc.InvalidRequestError( 
    731                            "A value is required for bind parameter %r" 
    732                            % bindparam.key, 
    733                            code="cd3x", 
    734                        ) 
    735 
    736                elif bindparam.callable: 
    737                    pd[name] = bindparam.effective_value 
    738                else: 
    739                    pd[name] = bindparam.value 
    740            return pd 
    741        else: 
    742            pd = {} 
    743            for bindparam in self.bind_names: 
    744                if _check and bindparam.required: 
    745                    if _group_number: 
    746                        raise exc.InvalidRequestError( 
    747                            "A value is required for bind parameter %r, " 
    748                            "in parameter group %d" 
    749                            % (bindparam.key, _group_number), 
    750                            code="cd3x", 
    751                        ) 
    752                    else: 
    753                        raise exc.InvalidRequestError( 
    754                            "A value is required for bind parameter %r" 
    755                            % bindparam.key, 
    756                            code="cd3x", 
    757                        ) 
    758 
    759                if bindparam.callable: 
    760                    pd[self.bind_names[bindparam]] = bindparam.effective_value 
    761                else: 
    762                    pd[self.bind_names[bindparam]] = bindparam.value 
    763            return pd 
    764 
    765    @property 
    766    def params(self): 
    767        """Return the bind param dictionary embedded into this 
    768        compiled object, for those values that are present.""" 
    769        return self.construct_params(_check=False) 
    770 
    771    @util.dependencies("sqlalchemy.engine.result") 
    772    def _create_result_map(self, result): 
    773        """utility method used for unit tests only.""" 
    774        return result.ResultMetaData._create_result_map(self._result_columns) 
    775 
    776    def default_from(self): 
    777        """Called when a SELECT statement has no froms, and no FROM clause is 
    778        to be appended. 
    779 
    780        Gives Oracle a chance to tack on a ``FROM DUAL`` to the string output. 
    781 
    782        """ 
    783        return "" 
    784 
    785    def visit_grouping(self, grouping, asfrom=False, **kwargs): 
    786        return "(" + grouping.element._compiler_dispatch(self, **kwargs) + ")" 
    787 
    788    def visit_label_reference( 
    789        self, element, within_columns_clause=False, **kwargs 
    790    ): 
    791        if self.stack and self.dialect.supports_simple_order_by_label: 
    792            selectable = self.stack[-1]["selectable"] 
    793 
    794            with_cols, only_froms, only_cols = selectable._label_resolve_dict 
    795            if within_columns_clause: 
    796                resolve_dict = only_froms 
    797            else: 
    798                resolve_dict = only_cols 
    799 
    800            # this can be None in the case that a _label_reference() 
    801            # were subject to a replacement operation, in which case 
    802            # the replacement of the Label element may have changed 
    803            # to something else like a ColumnClause expression. 
    804            order_by_elem = element.element._order_by_label_element 
    805 
    806            if ( 
    807                order_by_elem is not None 
    808                and order_by_elem.name in resolve_dict 
    809                and order_by_elem.shares_lineage( 
    810                    resolve_dict[order_by_elem.name] 
    811                ) 
    812            ): 
    813                kwargs[ 
    814                    "render_label_as_label" 
    815                ] = element.element._order_by_label_element 
    816        return self.process( 
    817            element.element, 
    818            within_columns_clause=within_columns_clause, 
    819            **kwargs 
    820        ) 
    821 
    822    def visit_textual_label_reference( 
    823        self, element, within_columns_clause=False, **kwargs 
    824    ): 
    825        if not self.stack: 
    826            # compiling the element outside of the context of a SELECT 
    827            return self.process(element._text_clause) 
    828 
    829        selectable = self.stack[-1]["selectable"] 
    830        with_cols, only_froms, only_cols = selectable._label_resolve_dict 
    831        try: 
    832            if within_columns_clause: 
    833                col = only_froms[element.element] 
    834            else: 
    835                col = with_cols[element.element] 
    836        except KeyError as ke: 
    837            elements._no_text_coercion( 
    838                element.element, 
    839                exc.CompileError, 
    840                "Can't resolve label reference for ORDER BY / " 
    841                "GROUP BY / DISTINCT etc.", 
    842                err=ke, 
    843            ) 
    844        else: 
    845            kwargs["render_label_as_label"] = col 
    846            return self.process( 
    847                col, within_columns_clause=within_columns_clause, **kwargs 
    848            ) 
    849 
    850    def visit_label( 
    851        self, 
    852        label, 
    853        add_to_result_map=None, 
    854        within_label_clause=False, 
    855        within_columns_clause=False, 
    856        render_label_as_label=None, 
    857        **kw 
    858    ): 
    859        # only render labels within the columns clause 
    860        # or ORDER BY clause of a select.  dialect-specific compilers 
    861        # can modify this behavior. 
    862        render_label_with_as = ( 
    863            within_columns_clause and not within_label_clause 
    864        ) 
    865        render_label_only = render_label_as_label is label 
    866 
    867        if render_label_only or render_label_with_as: 
    868            if isinstance(label.name, elements._truncated_label): 
    869                labelname = self._truncated_identifier("colident", label.name) 
    870            else: 
    871                labelname = label.name 
    872 
    873        if render_label_with_as: 
    874            if add_to_result_map is not None: 
    875                add_to_result_map( 
    876                    labelname, 
    877                    label.name, 
    878                    (label, labelname) + label._alt_names, 
    879                    label.type, 
    880                ) 
    881 
    882            return ( 
    883                label.element._compiler_dispatch( 
    884                    self, 
    885                    within_columns_clause=True, 
    886                    within_label_clause=True, 
    887                    **kw 
    888                ) 
    889                + OPERATORS[operators.as_] 
    890                + self.preparer.format_label(label, labelname) 
    891            ) 
    892        elif render_label_only: 
    893            return self.preparer.format_label(label, labelname) 
    894        else: 
    895            return label.element._compiler_dispatch( 
    896                self, within_columns_clause=False, **kw 
    897            ) 
    898 
    899    def _fallback_column_name(self, column): 
    900        raise exc.CompileError( 
    901            "Cannot compile Column object until " "its 'name' is assigned." 
    902        ) 
    903 
    904    def visit_column( 
    905        self, column, add_to_result_map=None, include_table=True, **kwargs 
    906    ): 
    907        name = orig_name = column.name 
    908        if name is None: 
    909            name = self._fallback_column_name(column) 
    910 
    911        is_literal = column.is_literal 
    912        if not is_literal and isinstance(name, elements._truncated_label): 
    913            name = self._truncated_identifier("colident", name) 
    914 
    915        if add_to_result_map is not None: 
    916            add_to_result_map( 
    917                name, orig_name, (column, name, column.key), column.type 
    918            ) 
    919 
    920        if is_literal: 
    921            # note we are not currently accommodating for 
    922            # literal_column(quoted_name('ident', True)) here 
    923            name = self.escape_literal_column(name) 
    924        else: 
    925            name = self.preparer.quote(name) 
    926        table = column.table 
    927        if table is None or not include_table or not table.named_with_column: 
    928            return name 
    929        else: 
    930            effective_schema = self.preparer.schema_for_object(table) 
    931 
    932            if effective_schema: 
    933                schema_prefix = ( 
    934                    self.preparer.quote_schema(effective_schema) + "." 
    935                ) 
    936            else: 
    937                schema_prefix = "" 
    938            tablename = table.name 
    939            if isinstance(tablename, elements._truncated_label): 
    940                tablename = self._truncated_identifier("alias", tablename) 
    941 
    942            return schema_prefix + self.preparer.quote(tablename) + "." + name 
    943 
    944    def visit_collation(self, element, **kw): 
    945        return self.preparer.format_collation(element.collation) 
    946 
    947    def visit_fromclause(self, fromclause, **kwargs): 
    948        return fromclause.name 
    949 
    950    def visit_index(self, index, **kwargs): 
    951        return index.name 
    952 
    953    def visit_typeclause(self, typeclause, **kw): 
    954        kw["type_expression"] = typeclause 
    955        return self.dialect.type_compiler.process(typeclause.type, **kw) 
    956 
    957    def post_process_text(self, text): 
    958        if self.preparer._double_percents: 
    959            text = text.replace("%", "%%") 
    960        return text 
    961 
    962    def escape_literal_column(self, text): 
    963        if self.preparer._double_percents: 
    964            text = text.replace("%", "%%") 
    965        return text 
    966 
    967    def visit_textclause(self, textclause, **kw): 
    968        def do_bindparam(m): 
    969            name = m.group(1) 
    970            if name in textclause._bindparams: 
    971                return self.process(textclause._bindparams[name], **kw) 
    972            else: 
    973                return self.bindparam_string(name, **kw) 
    974 
    975        if not self.stack: 
    976            self.isplaintext = True 
    977 
    978        # un-escape any \:params 
    979        return BIND_PARAMS_ESC.sub( 
    980            lambda m: m.group(1), 
    981            BIND_PARAMS.sub( 
    982                do_bindparam, self.post_process_text(textclause.text) 
    983            ), 
    984        ) 
    985 
    986    def visit_text_as_from( 
    987        self, taf, compound_index=None, asfrom=False, parens=True, **kw 
    988    ): 
    989 
    990        toplevel = not self.stack 
    991        entry = self._default_stack_entry if toplevel else self.stack[-1] 
    992 
    993        populate_result_map = ( 
    994            toplevel 
    995            or ( 
    996                compound_index == 0 
    997                and entry.get("need_result_map_for_compound", False) 
    998            ) 
    999            or entry.get("need_result_map_for_nested", False) 
    1000        ) 
    1001 
    1002        if populate_result_map: 
    1003            self._ordered_columns = ( 
    1004                self._textual_ordered_columns 
    1005            ) = taf.positional 
    1006            for c in taf.column_args: 
    1007                self.process( 
    1008                    c, 
    1009                    within_columns_clause=True, 
    1010                    add_to_result_map=self._add_to_result_map, 
    1011                ) 
    1012 
    1013        text = self.process(taf.element, **kw) 
    1014        if asfrom and parens: 
    1015            text = "(%s)" % text 
    1016        return text 
    1017 
    1018    def visit_null(self, expr, **kw): 
    1019        return "NULL" 
    1020 
    1021    def visit_true(self, expr, **kw): 
    1022        if self.dialect.supports_native_boolean: 
    1023            return "true" 
    1024        else: 
    1025            return "1" 
    1026 
    1027    def visit_false(self, expr, **kw): 
    1028        if self.dialect.supports_native_boolean: 
    1029            return "false" 
    1030        else: 
    1031            return "0" 
    1032 
    1033    def visit_clauselist(self, clauselist, **kw): 
    1034        sep = clauselist.operator 
    1035        if sep is None: 
    1036            sep = " " 
    1037        else: 
    1038            sep = OPERATORS[clauselist.operator] 
    1039 
    1040        text = sep.join( 
    1041            s 
    1042            for s in ( 
    1043                c._compiler_dispatch(self, **kw) for c in clauselist.clauses 
    1044            ) 
    1045            if s 
    1046        ) 
    1047        if clauselist._tuple_values and self.dialect.tuple_in_values: 
    1048            text = "VALUES " + text 
    1049        return text 
    1050 
    1051    def visit_case(self, clause, **kwargs): 
    1052        x = "CASE " 
    1053        if clause.value is not None: 
    1054            x += clause.value._compiler_dispatch(self, **kwargs) + " " 
    1055        for cond, result in clause.whens: 
    1056            x += ( 
    1057                "WHEN " 
    1058                + cond._compiler_dispatch(self, **kwargs) 
    1059                + " THEN " 
    1060                + result._compiler_dispatch(self, **kwargs) 
    1061                + " " 
    1062            ) 
    1063        if clause.else_ is not None: 
    1064            x += ( 
    1065                "ELSE " + clause.else_._compiler_dispatch(self, **kwargs) + " " 
    1066            ) 
    1067        x += "END" 
    1068        return x 
    1069 
    1070    def visit_type_coerce(self, type_coerce, **kw): 
    1071        return type_coerce.typed_expression._compiler_dispatch(self, **kw) 
    1072 
    1073    def visit_cast(self, cast, **kwargs): 
    1074        return "CAST(%s AS %s)" % ( 
    1075            cast.clause._compiler_dispatch(self, **kwargs), 
    1076            cast.typeclause._compiler_dispatch(self, **kwargs), 
    1077        ) 
    1078 
    1079    def _format_frame_clause(self, range_, **kw): 
    1080 
    1081        return "%s AND %s" % ( 
    1082            "UNBOUNDED PRECEDING" 
    1083            if range_[0] is elements.RANGE_UNBOUNDED 
    1084            else "CURRENT ROW" 
    1085            if range_[0] is elements.RANGE_CURRENT 
    1086            else "%s PRECEDING" 
    1087            % (self.process(elements.literal(abs(range_[0])), **kw),) 
    1088            if range_[0] < 0 
    1089            else "%s FOLLOWING" 
    1090            % (self.process(elements.literal(range_[0]), **kw),), 
    1091            "UNBOUNDED FOLLOWING" 
    1092            if range_[1] is elements.RANGE_UNBOUNDED 
    1093            else "CURRENT ROW" 
    1094            if range_[1] is elements.RANGE_CURRENT 
    1095            else "%s PRECEDING" 
    1096            % (self.process(elements.literal(abs(range_[1])), **kw),) 
    1097            if range_[1] < 0 
    1098            else "%s FOLLOWING" 
    1099            % (self.process(elements.literal(range_[1]), **kw),), 
    1100        ) 
    1101 
    1102    def visit_over(self, over, **kwargs): 
    1103        if over.range_: 
    1104            range_ = "RANGE BETWEEN %s" % self._format_frame_clause( 
    1105                over.range_, **kwargs 
    1106            ) 
    1107        elif over.rows: 
    1108            range_ = "ROWS BETWEEN %s" % self._format_frame_clause( 
    1109                over.rows, **kwargs 
    1110            ) 
    1111        else: 
    1112            range_ = None 
    1113 
    1114        return "%s OVER (%s)" % ( 
    1115            over.element._compiler_dispatch(self, **kwargs), 
    1116            " ".join( 
    1117                [ 
    1118                    "%s BY %s" 
    1119                    % (word, clause._compiler_dispatch(self, **kwargs)) 
    1120                    for word, clause in ( 
    1121                        ("PARTITION", over.partition_by), 
    1122                        ("ORDER", over.order_by), 
    1123                    ) 
    1124                    if clause is not None and len(clause) 
    1125                ] 
    1126                + ([range_] if range_ else []) 
    1127            ), 
    1128        ) 
    1129 
    1130    def visit_withingroup(self, withingroup, **kwargs): 
    1131        return "%s WITHIN GROUP (ORDER BY %s)" % ( 
    1132            withingroup.element._compiler_dispatch(self, **kwargs), 
    1133            withingroup.order_by._compiler_dispatch(self, **kwargs), 
    1134        ) 
    1135 
    1136    def visit_funcfilter(self, funcfilter, **kwargs): 
    1137        return "%s FILTER (WHERE %s)" % ( 
    1138            funcfilter.func._compiler_dispatch(self, **kwargs), 
    1139            funcfilter.criterion._compiler_dispatch(self, **kwargs), 
    1140        ) 
    1141 
    1142    def visit_extract(self, extract, **kwargs): 
    1143        field = self.extract_map.get(extract.field, extract.field) 
    1144        return "EXTRACT(%s FROM %s)" % ( 
    1145            field, 
    1146            extract.expr._compiler_dispatch(self, **kwargs), 
    1147        ) 
    1148 
    1149    def visit_function(self, func, add_to_result_map=None, **kwargs): 
    1150        if add_to_result_map is not None: 
    1151            add_to_result_map(func.name, func.name, (), func.type) 
    1152 
    1153        disp = getattr(self, "visit_%s_func" % func.name.lower(), None) 
    1154        if disp: 
    1155            return disp(func, **kwargs) 
    1156        else: 
    1157            name = FUNCTIONS.get(func.__class__, None) 
    1158            if name: 
    1159                if func._has_args: 
    1160                    name += "%(expr)s" 
    1161            else: 
    1162                name = func.name 
    1163                name = ( 
    1164                    self.preparer.quote(name) 
    1165                    if self.preparer._requires_quotes_illegal_chars(name) 
    1166                    or isinstance(name, elements.quoted_name) 
    1167                    else name 
    1168                ) 
    1169                name = name + "%(expr)s" 
    1170            return ".".join( 
    1171                [ 
    1172                    ( 
    1173                        self.preparer.quote(tok) 
    1174                        if self.preparer._requires_quotes_illegal_chars(tok) 
    1175                        or isinstance(name, elements.quoted_name) 
    1176                        else tok 
    1177                    ) 
    1178                    for tok in func.packagenames 
    1179                ] 
    1180                + [name] 
    1181            ) % {"expr": self.function_argspec(func, **kwargs)} 
    1182 
    1183    def visit_next_value_func(self, next_value, **kw): 
    1184        return self.visit_sequence(next_value.sequence) 
    1185 
    1186    def visit_sequence(self, sequence, **kw): 
    1187        raise NotImplementedError( 
    1188            "Dialect '%s' does not support sequence increments." 
    1189            % self.dialect.name 
    1190        ) 
    1191 
    1192    def function_argspec(self, func, **kwargs): 
    1193        return func.clause_expr._compiler_dispatch(self, **kwargs) 
    1194 
    1195    def visit_compound_select( 
    1196        self, cs, asfrom=False, parens=True, compound_index=0, **kwargs 
    1197    ): 
    1198        toplevel = not self.stack 
    1199        entry = self._default_stack_entry if toplevel else self.stack[-1] 
    1200        need_result_map = toplevel or ( 
    1201            compound_index == 0 
    1202            and entry.get("need_result_map_for_compound", False) 
    1203        ) 
    1204 
    1205        self.stack.append( 
    1206            { 
    1207                "correlate_froms": entry["correlate_froms"], 
    1208                "asfrom_froms": entry["asfrom_froms"], 
    1209                "selectable": cs, 
    1210                "need_result_map_for_compound": need_result_map, 
    1211            } 
    1212        ) 
    1213 
    1214        keyword = self.compound_keywords.get(cs.keyword) 
    1215 
    1216        text = (" " + keyword + " ").join( 
    1217            ( 
    1218                c._compiler_dispatch( 
    1219                    self, 
    1220                    asfrom=asfrom, 
    1221                    parens=False, 
    1222                    compound_index=i, 
    1223                    **kwargs 
    1224                ) 
    1225                for i, c in enumerate(cs.selects) 
    1226            ) 
    1227        ) 
    1228 
    1229        text += self.group_by_clause(cs, **dict(asfrom=asfrom, **kwargs)) 
    1230        text += self.order_by_clause(cs, **kwargs) 
    1231        text += ( 
    1232            (cs._limit_clause is not None or cs._offset_clause is not None) 
    1233            and self.limit_clause(cs, **kwargs) 
    1234            or "" 
    1235        ) 
    1236 
    1237        if self.ctes and toplevel: 
    1238            text = self._render_cte_clause() + text 
    1239 
    1240        self.stack.pop(-1) 
    1241        if asfrom and parens: 
    1242            return "(" + text + ")" 
    1243        else: 
    1244            return text 
    1245 
    1246    def _get_operator_dispatch(self, operator_, qualifier1, qualifier2): 
    1247        attrname = "visit_%s_%s%s" % ( 
    1248            operator_.__name__, 
    1249            qualifier1, 
    1250            "_" + qualifier2 if qualifier2 else "", 
    1251        ) 
    1252        return getattr(self, attrname, None) 
    1253 
    1254    def visit_unary(self, unary, **kw): 
    1255        if unary.operator: 
    1256            if unary.modifier: 
    1257                raise exc.CompileError( 
    1258                    "Unary expression does not support operator " 
    1259                    "and modifier simultaneously" 
    1260                ) 
    1261            disp = self._get_operator_dispatch( 
    1262                unary.operator, "unary", "operator" 
    1263            ) 
    1264            if disp: 
    1265                return disp(unary, unary.operator, **kw) 
    1266            else: 
    1267                return self._generate_generic_unary_operator( 
    1268                    unary, OPERATORS[unary.operator], **kw 
    1269                ) 
    1270        elif unary.modifier: 
    1271            disp = self._get_operator_dispatch( 
    1272                unary.modifier, "unary", "modifier" 
    1273            ) 
    1274            if disp: 
    1275                return disp(unary, unary.modifier, **kw) 
    1276            else: 
    1277                return self._generate_generic_unary_modifier( 
    1278                    unary, OPERATORS[unary.modifier], **kw 
    1279                ) 
    1280        else: 
    1281            raise exc.CompileError( 
    1282                "Unary expression has no operator or modifier" 
    1283            ) 
    1284 
    1285    def visit_istrue_unary_operator(self, element, operator, **kw): 
    1286        if ( 
    1287            element._is_implicitly_boolean 
    1288            or self.dialect.supports_native_boolean 
    1289        ): 
    1290            return self.process(element.element, **kw) 
    1291        else: 
    1292            return "%s = 1" % self.process(element.element, **kw) 
    1293 
    1294    def visit_isfalse_unary_operator(self, element, operator, **kw): 
    1295        if ( 
    1296            element._is_implicitly_boolean 
    1297            or self.dialect.supports_native_boolean 
    1298        ): 
    1299            return "NOT %s" % self.process(element.element, **kw) 
    1300        else: 
    1301            return "%s = 0" % self.process(element.element, **kw) 
    1302 
    1303    def visit_notmatch_op_binary(self, binary, operator, **kw): 
    1304        return "NOT %s" % self.visit_binary( 
    1305            binary, override_operator=operators.match_op 
    1306        ) 
    1307 
    1308    def _emit_empty_in_warning(self): 
    1309        util.warn( 
    1310            "The IN-predicate was invoked with an " 
    1311            "empty sequence. This results in a " 
    1312            "contradiction, which nonetheless can be " 
    1313            "expensive to evaluate.  Consider alternative " 
    1314            "strategies for improved performance." 
    1315        ) 
    1316 
    1317    def visit_empty_in_op_binary(self, binary, operator, **kw): 
    1318        if self.dialect._use_static_in: 
    1319            return "1 != 1" 
    1320        else: 
    1321            if self.dialect._warn_on_empty_in: 
    1322                self._emit_empty_in_warning() 
    1323            return self.process(binary.left != binary.left) 
    1324 
    1325    def visit_empty_notin_op_binary(self, binary, operator, **kw): 
    1326        if self.dialect._use_static_in: 
    1327            return "1 = 1" 
    1328        else: 
    1329            if self.dialect._warn_on_empty_in: 
    1330                self._emit_empty_in_warning() 
    1331            return self.process(binary.left == binary.left) 
    1332 
    1333    def visit_empty_set_expr(self, element_types): 
    1334        raise NotImplementedError( 
    1335            "Dialect '%s' does not support empty set expression." 
    1336            % self.dialect.name 
    1337        ) 
    1338 
    1339    def visit_binary( 
    1340        self, binary, override_operator=None, eager_grouping=False, **kw 
    1341    ): 
    1342 
    1343        # don't allow "? = ?" to render 
    1344        if ( 
    1345            self.ansi_bind_rules 
    1346            and isinstance(binary.left, elements.BindParameter) 
    1347            and isinstance(binary.right, elements.BindParameter) 
    1348        ): 
    1349            kw["literal_binds"] = True 
    1350 
    1351        operator_ = override_operator or binary.operator 
    1352        disp = self._get_operator_dispatch(operator_, "binary", None) 
    1353        if disp: 
    1354            return disp(binary, operator_, **kw) 
    1355        else: 
    1356            try: 
    1357                opstring = OPERATORS[operator_] 
    1358            except KeyError as err: 
    1359                util.raise_( 
    1360                    exc.UnsupportedCompilationError(self, operator_), 
    1361                    replace_context=err, 
    1362                ) 
    1363            else: 
    1364                return self._generate_generic_binary(binary, opstring, **kw) 
    1365 
    1366    def visit_function_as_comparison_op_binary(self, element, operator, **kw): 
    1367        return self.process(element.sql_function, **kw) 
    1368 
    1369    def visit_mod_binary(self, binary, operator, **kw): 
    1370        if self.preparer._double_percents: 
    1371            return ( 
    1372                self.process(binary.left, **kw) 
    1373                + " %% " 
    1374                + self.process(binary.right, **kw) 
    1375            ) 
    1376        else: 
    1377            return ( 
    1378                self.process(binary.left, **kw) 
    1379                + " % " 
    1380                + self.process(binary.right, **kw) 
    1381            ) 
    1382 
    1383    def visit_custom_op_binary(self, element, operator, **kw): 
    1384        kw["eager_grouping"] = operator.eager_grouping 
    1385        return self._generate_generic_binary( 
    1386            element, " " + operator.opstring + " ", **kw 
    1387        ) 
    1388 
    1389    def visit_custom_op_unary_operator(self, element, operator, **kw): 
    1390        return self._generate_generic_unary_operator( 
    1391            element, operator.opstring + " ", **kw 
    1392        ) 
    1393 
    1394    def visit_custom_op_unary_modifier(self, element, operator, **kw): 
    1395        return self._generate_generic_unary_modifier( 
    1396            element, " " + operator.opstring, **kw 
    1397        ) 
    1398 
    1399    def _generate_generic_binary( 
    1400        self, binary, opstring, eager_grouping=False, **kw 
    1401    ): 
    1402 
    1403        _in_binary = kw.get("_in_binary", False) 
    1404 
    1405        kw["_in_binary"] = True 
    1406        text = ( 
    1407            binary.left._compiler_dispatch( 
    1408                self, eager_grouping=eager_grouping, **kw 
    1409            ) 
    1410            + opstring 
    1411            + binary.right._compiler_dispatch( 
    1412                self, eager_grouping=eager_grouping, **kw 
    1413            ) 
    1414        ) 
    1415 
    1416        if _in_binary and eager_grouping: 
    1417            text = "(%s)" % text 
    1418        return text 
    1419 
    1420    def _generate_generic_unary_operator(self, unary, opstring, **kw): 
    1421        return opstring + unary.element._compiler_dispatch(self, **kw) 
    1422 
    1423    def _generate_generic_unary_modifier(self, unary, opstring, **kw): 
    1424        return unary.element._compiler_dispatch(self, **kw) + opstring 
    1425 
    1426    @util.memoized_property 
    1427    def _like_percent_literal(self): 
    1428        return elements.literal_column("'%'", type_=sqltypes.STRINGTYPE) 
    1429 
    1430    def visit_contains_op_binary(self, binary, operator, **kw): 
    1431        binary = binary._clone() 
    1432        percent = self._like_percent_literal 
    1433        binary.right = percent.__add__(binary.right).__add__(percent) 
    1434        return self.visit_like_op_binary(binary, operator, **kw) 
    1435 
    1436    def visit_notcontains_op_binary(self, binary, operator, **kw): 
    1437        binary = binary._clone() 
    1438        percent = self._like_percent_literal 
    1439        binary.right = percent.__add__(binary.right).__add__(percent) 
    1440        return self.visit_notlike_op_binary(binary, operator, **kw) 
    1441 
    1442    def visit_startswith_op_binary(self, binary, operator, **kw): 
    1443        binary = binary._clone() 
    1444        percent = self._like_percent_literal 
    1445        binary.right = percent.__radd__(binary.right) 
    1446        return self.visit_like_op_binary(binary, operator, **kw) 
    1447 
    1448    def visit_notstartswith_op_binary(self, binary, operator, **kw): 
    1449        binary = binary._clone() 
    1450        percent = self._like_percent_literal 
    1451        binary.right = percent.__radd__(binary.right) 
    1452        return self.visit_notlike_op_binary(binary, operator, **kw) 
    1453 
    1454    def visit_endswith_op_binary(self, binary, operator, **kw): 
    1455        binary = binary._clone() 
    1456        percent = self._like_percent_literal 
    1457        binary.right = percent.__add__(binary.right) 
    1458        return self.visit_like_op_binary(binary, operator, **kw) 
    1459 
    1460    def visit_notendswith_op_binary(self, binary, operator, **kw): 
    1461        binary = binary._clone() 
    1462        percent = self._like_percent_literal 
    1463        binary.right = percent.__add__(binary.right) 
    1464        return self.visit_notlike_op_binary(binary, operator, **kw) 
    1465 
    1466    def visit_like_op_binary(self, binary, operator, **kw): 
    1467        escape = binary.modifiers.get("escape", None) 
    1468 
    1469        # TODO: use ternary here, not "and"/ "or" 
    1470        return "%s LIKE %s" % ( 
    1471            binary.left._compiler_dispatch(self, **kw), 
    1472            binary.right._compiler_dispatch(self, **kw), 
    1473        ) + ( 
    1474            " ESCAPE " + self.render_literal_value(escape, sqltypes.STRINGTYPE) 
    1475            if escape 
    1476            else "" 
    1477        ) 
    1478 
    1479    def visit_notlike_op_binary(self, binary, operator, **kw): 
    1480        escape = binary.modifiers.get("escape", None) 
    1481        return "%s NOT LIKE %s" % ( 
    1482            binary.left._compiler_dispatch(self, **kw), 
    1483            binary.right._compiler_dispatch(self, **kw), 
    1484        ) + ( 
    1485            " ESCAPE " + self.render_literal_value(escape, sqltypes.STRINGTYPE) 
    1486            if escape 
    1487            else "" 
    1488        ) 
    1489 
    1490    def visit_ilike_op_binary(self, binary, operator, **kw): 
    1491        escape = binary.modifiers.get("escape", None) 
    1492        return "lower(%s) LIKE lower(%s)" % ( 
    1493            binary.left._compiler_dispatch(self, **kw), 
    1494            binary.right._compiler_dispatch(self, **kw), 
    1495        ) + ( 
    1496            " ESCAPE " + self.render_literal_value(escape, sqltypes.STRINGTYPE) 
    1497            if escape 
    1498            else "" 
    1499        ) 
    1500 
    1501    def visit_notilike_op_binary(self, binary, operator, **kw): 
    1502        escape = binary.modifiers.get("escape", None) 
    1503        return "lower(%s) NOT LIKE lower(%s)" % ( 
    1504            binary.left._compiler_dispatch(self, **kw), 
    1505            binary.right._compiler_dispatch(self, **kw), 
    1506        ) + ( 
    1507            " ESCAPE " + self.render_literal_value(escape, sqltypes.STRINGTYPE) 
    1508            if escape 
    1509            else "" 
    1510        ) 
    1511 
    1512    def visit_between_op_binary(self, binary, operator, **kw): 
    1513        symmetric = binary.modifiers.get("symmetric", False) 
    1514        return self._generate_generic_binary( 
    1515            binary, " BETWEEN SYMMETRIC " if symmetric else " BETWEEN ", **kw 
    1516        ) 
    1517 
    1518    def visit_notbetween_op_binary(self, binary, operator, **kw): 
    1519        symmetric = binary.modifiers.get("symmetric", False) 
    1520        return self._generate_generic_binary( 
    1521            binary, 
    1522            " NOT BETWEEN SYMMETRIC " if symmetric else " NOT BETWEEN ", 
    1523            **kw 
    1524        ) 
    1525 
    1526    def visit_bindparam( 
    1527        self, 
    1528        bindparam, 
    1529        within_columns_clause=False, 
    1530        literal_binds=False, 
    1531        skip_bind_expression=False, 
    1532        **kwargs 
    1533    ): 
    1534 
    1535        if not skip_bind_expression: 
    1536            impl = bindparam.type.dialect_impl(self.dialect) 
    1537            if impl._has_bind_expression: 
    1538                bind_expression = impl.bind_expression(bindparam) 
    1539                return self.process( 
    1540                    bind_expression, 
    1541                    skip_bind_expression=True, 
    1542                    within_columns_clause=within_columns_clause, 
    1543                    literal_binds=literal_binds, 
    1544                    **kwargs 
    1545                ) 
    1546 
    1547        if literal_binds or (within_columns_clause and self.ansi_bind_rules): 
    1548            if bindparam.value is None and bindparam.callable is None: 
    1549                raise exc.CompileError( 
    1550                    "Bind parameter '%s' without a " 
    1551                    "renderable value not allowed here." % bindparam.key 
    1552                ) 
    1553            return self.render_literal_bindparam( 
    1554                bindparam, within_columns_clause=True, **kwargs 
    1555            ) 
    1556 
    1557        name = self._truncate_bindparam(bindparam) 
    1558 
    1559        if name in self.binds: 
    1560            existing = self.binds[name] 
    1561            if existing is not bindparam: 
    1562                if ( 
    1563                    existing.unique or bindparam.unique 
    1564                ) and not existing.proxy_set.intersection(bindparam.proxy_set): 
    1565                    raise exc.CompileError( 
    1566                        "Bind parameter '%s' conflicts with " 
    1567                        "unique bind parameter of the same name" 
    1568                        % bindparam.key 
    1569                    ) 
    1570                elif existing._is_crud or bindparam._is_crud: 
    1571                    raise exc.CompileError( 
    1572                        "bindparam() name '%s' is reserved " 
    1573                        "for automatic usage in the VALUES or SET " 
    1574                        "clause of this " 
    1575                        "insert/update statement.   Please use a " 
    1576                        "name other than column name when using bindparam() " 
    1577                        "with insert() or update() (for example, 'b_%s')." 
    1578                        % (bindparam.key, bindparam.key) 
    1579                    ) 
    1580 
    1581        self.binds[bindparam.key] = self.binds[name] = bindparam 
    1582 
    1583        return self.bindparam_string( 
    1584            name, expanding=bindparam.expanding, **kwargs 
    1585        ) 
    1586 
    1587    def render_literal_bindparam(self, bindparam, **kw): 
    1588        value = bindparam.effective_value 
    1589        return self.render_literal_value(value, bindparam.type) 
    1590 
    1591    def render_literal_value(self, value, type_): 
    1592        """Render the value of a bind parameter as a quoted literal. 
    1593 
    1594        This is used for statement sections that do not accept bind parameters 
    1595        on the target driver/database. 
    1596 
    1597        This should be implemented by subclasses using the quoting services 
    1598        of the DBAPI. 
    1599 
    1600        """ 
    1601 
    1602        processor = type_._cached_literal_processor(self.dialect) 
    1603        if processor: 
    1604            return processor(value) 
    1605        else: 
    1606            raise NotImplementedError( 
    1607                "Don't know how to literal-quote value %r" % value 
    1608            ) 
    1609 
    1610    def _truncate_bindparam(self, bindparam): 
    1611        if bindparam in self.bind_names: 
    1612            return self.bind_names[bindparam] 
    1613 
    1614        bind_name = bindparam.key 
    1615        if isinstance(bind_name, elements._truncated_label): 
    1616            bind_name = self._truncated_identifier("bindparam", bind_name) 
    1617 
    1618        # add to bind_names for translation 
    1619        self.bind_names[bindparam] = bind_name 
    1620 
    1621        return bind_name 
    1622 
    1623    def _truncated_identifier(self, ident_class, name): 
    1624        if (ident_class, name) in self.truncated_names: 
    1625            return self.truncated_names[(ident_class, name)] 
    1626 
    1627        anonname = name.apply_map(self.anon_map) 
    1628 
    1629        if len(anonname) > self.label_length - 6: 
    1630            counter = self.truncated_names.get(ident_class, 1) 
    1631            truncname = ( 
    1632                anonname[0 : max(self.label_length - 6, 0)] 
    1633                + "_" 
    1634                + hex(counter)[2:] 
    1635            ) 
    1636            self.truncated_names[ident_class] = counter + 1 
    1637        else: 
    1638            truncname = anonname 
    1639        self.truncated_names[(ident_class, name)] = truncname 
    1640        return truncname 
    1641 
    1642    def _anonymize(self, name): 
    1643        return name % self.anon_map 
    1644 
    1645    def bindparam_string( 
    1646        self, name, positional_names=None, expanding=False, **kw 
    1647    ): 
    1648        if self.positional: 
    1649            if positional_names is not None: 
    1650                positional_names.append(name) 
    1651            else: 
    1652                self.positiontup.append(name) 
    1653        if expanding: 
    1654            self.contains_expanding_parameters = True 
    1655            return "([EXPANDING_%s])" % name 
    1656        else: 
    1657            return self.bindtemplate % {"name": name} 
    1658 
    1659    def visit_cte( 
    1660        self, 
    1661        cte, 
    1662        asfrom=False, 
    1663        ashint=False, 
    1664        fromhints=None, 
    1665        visiting_cte=None, 
    1666        **kwargs 
    1667    ): 
    1668        self._init_cte_state() 
    1669 
    1670        kwargs["visiting_cte"] = cte 
    1671        if isinstance(cte.name, elements._truncated_label): 
    1672            cte_name = self._truncated_identifier("alias", cte.name) 
    1673        else: 
    1674            cte_name = cte.name 
    1675 
    1676        is_new_cte = True 
    1677        embedded_in_current_named_cte = False 
    1678 
    1679        if cte_name in self.ctes_by_name: 
    1680            existing_cte = self.ctes_by_name[cte_name] 
    1681            embedded_in_current_named_cte = visiting_cte is existing_cte 
    1682 
    1683            # we've generated a same-named CTE that we are enclosed in, 
    1684            # or this is the same CTE.  just return the name. 
    1685            if cte in existing_cte._restates or cte is existing_cte: 
    1686                is_new_cte = False 
    1687            elif existing_cte in cte._restates: 
    1688                # we've generated a same-named CTE that is 
    1689                # enclosed in us - we take precedence, so 
    1690                # discard the text for the "inner". 
    1691                del self.ctes[existing_cte] 
    1692            else: 
    1693                raise exc.CompileError( 
    1694                    "Multiple, unrelated CTEs found with " 
    1695                    "the same name: %r" % cte_name 
    1696                ) 
    1697 
    1698        if asfrom or is_new_cte: 
    1699            if cte._cte_alias is not None: 
    1700                pre_alias_cte = cte._cte_alias 
    1701                cte_pre_alias_name = cte._cte_alias.name 
    1702                if isinstance(cte_pre_alias_name, elements._truncated_label): 
    1703                    cte_pre_alias_name = self._truncated_identifier( 
    1704                        "alias", cte_pre_alias_name 
    1705                    ) 
    1706            else: 
    1707                pre_alias_cte = cte 
    1708                cte_pre_alias_name = None 
    1709 
    1710        if is_new_cte: 
    1711            self.ctes_by_name[cte_name] = cte 
    1712 
    1713            # look for embedded DML ctes and propagate autocommit 
    1714            if ( 
    1715                "autocommit" in cte.element._execution_options 
    1716                and "autocommit" not in self.execution_options 
    1717            ): 
    1718                self.execution_options = self.execution_options.union( 
    1719                    { 
    1720                        "autocommit": cte.element._execution_options[ 
    1721                            "autocommit" 
    1722                        ] 
    1723                    } 
    1724                ) 
    1725 
    1726            if pre_alias_cte not in self.ctes: 
    1727                self.visit_cte(pre_alias_cte, **kwargs) 
    1728 
    1729            if not cte_pre_alias_name and cte not in self.ctes: 
    1730                if cte.recursive: 
    1731                    self.ctes_recursive = True 
    1732                text = self.preparer.format_alias(cte, cte_name) 
    1733                if cte.recursive: 
    1734                    if isinstance(cte.original, selectable.Select): 
    1735                        col_source = cte.original 
    1736                    elif isinstance(cte.original, selectable.CompoundSelect): 
    1737                        col_source = cte.original.selects[0] 
    1738                    else: 
    1739                        assert False 
    1740                    recur_cols = [ 
    1741                        c 
    1742                        for c in util.unique_list(col_source.inner_columns) 
    1743                        if c is not None 
    1744                    ] 
    1745 
    1746                    text += "(%s)" % ( 
    1747                        ", ".join( 
    1748                            self.preparer.format_column(ident) 
    1749                            for ident in recur_cols 
    1750                        ) 
    1751                    ) 
    1752 
    1753                if self.positional: 
    1754                    kwargs["positional_names"] = self.cte_positional[cte] = [] 
    1755 
    1756                text += " AS %s\n%s" % ( 
    1757                    self._generate_prefixes(cte, cte._prefixes, **kwargs), 
    1758                    cte.original._compiler_dispatch( 
    1759                        self, asfrom=True, **kwargs 
    1760                    ), 
    1761                ) 
    1762 
    1763                if cte._suffixes: 
    1764                    text += " " + self._generate_prefixes( 
    1765                        cte, cte._suffixes, **kwargs 
    1766                    ) 
    1767 
    1768                self.ctes[cte] = text 
    1769 
    1770        if asfrom: 
    1771            if not is_new_cte and embedded_in_current_named_cte: 
    1772                return self.preparer.format_alias(cte, cte_name) 
    1773 
    1774            if cte_pre_alias_name: 
    1775                text = self.preparer.format_alias(cte, cte_pre_alias_name) 
    1776                if self.preparer._requires_quotes(cte_name): 
    1777                    cte_name = self.preparer.quote(cte_name) 
    1778                text += self.get_render_as_alias_suffix(cte_name) 
    1779                return text 
    1780            else: 
    1781                return self.preparer.format_alias(cte, cte_name) 
    1782 
    1783    def visit_alias( 
    1784        self, 
    1785        alias, 
    1786        asfrom=False, 
    1787        ashint=False, 
    1788        iscrud=False, 
    1789        fromhints=None, 
    1790        **kwargs 
    1791    ): 
    1792        if asfrom or ashint: 
    1793            if isinstance(alias.name, elements._truncated_label): 
    1794                alias_name = self._truncated_identifier("alias", alias.name) 
    1795            else: 
    1796                alias_name = alias.name 
    1797 
    1798        if ashint: 
    1799            return self.preparer.format_alias(alias, alias_name) 
    1800        elif asfrom: 
    1801            ret = alias.original._compiler_dispatch( 
    1802                self, asfrom=True, **kwargs 
    1803            ) + self.get_render_as_alias_suffix( 
    1804                self.preparer.format_alias(alias, alias_name) 
    1805            ) 
    1806 
    1807            if fromhints and alias in fromhints: 
    1808                ret = self.format_from_hint_text( 
    1809                    ret, alias, fromhints[alias], iscrud 
    1810                ) 
    1811 
    1812            return ret 
    1813        else: 
    1814            return alias.original._compiler_dispatch(self, **kwargs) 
    1815 
    1816    def visit_lateral(self, lateral, **kw): 
    1817        kw["lateral"] = True 
    1818        return "LATERAL %s" % self.visit_alias(lateral, **kw) 
    1819 
    1820    def visit_tablesample(self, tablesample, asfrom=False, **kw): 
    1821        text = "%s TABLESAMPLE %s" % ( 
    1822            self.visit_alias(tablesample, asfrom=True, **kw), 
    1823            tablesample._get_method()._compiler_dispatch(self, **kw), 
    1824        ) 
    1825 
    1826        if tablesample.seed is not None: 
    1827            text += " REPEATABLE (%s)" % ( 
    1828                tablesample.seed._compiler_dispatch(self, **kw) 
    1829            ) 
    1830 
    1831        return text 
    1832 
    1833    def get_render_as_alias_suffix(self, alias_name_text): 
    1834        return " AS " + alias_name_text 
    1835 
    1836    def _add_to_result_map(self, keyname, name, objects, type_): 
    1837        self._result_columns.append((keyname, name, objects, type_)) 
    1838 
    1839    def _label_select_column( 
    1840        self, 
    1841        select, 
    1842        column, 
    1843        populate_result_map, 
    1844        asfrom, 
    1845        column_clause_args, 
    1846        name=None, 
    1847        within_columns_clause=True, 
    1848        need_column_expressions=False, 
    1849    ): 
    1850        """produce labeled columns present in a select().""" 
    1851 
    1852        impl = column.type.dialect_impl(self.dialect) 
    1853 
    1854        if impl._has_column_expression and ( 
    1855            need_column_expressions or populate_result_map 
    1856        ): 
    1857            col_expr = impl.column_expression(column) 
    1858 
    1859            if populate_result_map: 
    1860 
    1861                def add_to_result_map(keyname, name, objects, type_): 
    1862                    self._add_to_result_map( 
    1863                        keyname, name, (column,) + objects, type_ 
    1864                    ) 
    1865 
    1866            else: 
    1867                add_to_result_map = None 
    1868        else: 
    1869            col_expr = column 
    1870            if populate_result_map: 
    1871                add_to_result_map = self._add_to_result_map 
    1872            else: 
    1873                add_to_result_map = None 
    1874 
    1875        if not within_columns_clause: 
    1876            result_expr = col_expr 
    1877        elif isinstance(column, elements.Label): 
    1878            if col_expr is not column: 
    1879                result_expr = _CompileLabel( 
    1880                    col_expr, column.name, alt_names=(column.element,) 
    1881                ) 
    1882            else: 
    1883                result_expr = col_expr 
    1884 
    1885        elif select is not None and name: 
    1886            result_expr = _CompileLabel( 
    1887                col_expr, name, alt_names=(column._key_label,) 
    1888            ) 
    1889 
    1890        elif ( 
    1891            asfrom 
    1892            and isinstance(column, elements.ColumnClause) 
    1893            and not column.is_literal 
    1894            and column.table is not None 
    1895            and not isinstance(column.table, selectable.Select) 
    1896        ): 
    1897            result_expr = _CompileLabel( 
    1898                col_expr, 
    1899                elements._as_truncated(column.name), 
    1900                alt_names=(column.key,), 
    1901            ) 
    1902        elif ( 
    1903            not isinstance(column, elements.TextClause) 
    1904            and ( 
    1905                not isinstance(column, elements.UnaryExpression) 
    1906                or column.wraps_column_expression 
    1907            ) 
    1908            and ( 
    1909                not hasattr(column, "name") 
    1910                or isinstance(column, functions.Function) 
    1911            ) 
    1912        ): 
    1913            result_expr = _CompileLabel(col_expr, column.anon_label) 
    1914        elif col_expr is not column: 
    1915            # TODO: are we sure "column" has a .name and .key here ? 
    1916            # assert isinstance(column, elements.ColumnClause) 
    1917            result_expr = _CompileLabel( 
    1918                col_expr, 
    1919                elements._as_truncated(column.name), 
    1920                alt_names=(column.key,), 
    1921            ) 
    1922        else: 
    1923            result_expr = col_expr 
    1924 
    1925        column_clause_args.update( 
    1926            within_columns_clause=within_columns_clause, 
    1927            add_to_result_map=add_to_result_map, 
    1928        ) 
    1929        return result_expr._compiler_dispatch(self, **column_clause_args) 
    1930 
    1931    def format_from_hint_text(self, sqltext, table, hint, iscrud): 
    1932        hinttext = self.get_from_hint_text(table, hint) 
    1933        if hinttext: 
    1934            sqltext += " " + hinttext 
    1935        return sqltext 
    1936 
    1937    def get_select_hint_text(self, byfroms): 
    1938        return None 
    1939 
    1940    def get_from_hint_text(self, table, text): 
    1941        return None 
    1942 
    1943    def get_crud_hint_text(self, table, text): 
    1944        return None 
    1945 
    1946    def get_statement_hint_text(self, hint_texts): 
    1947        return " ".join(hint_texts) 
    1948 
    1949    def _transform_select_for_nested_joins(self, select): 
    1950        """Rewrite any "a JOIN (b JOIN c)" expression as 
    1951        "a JOIN (select * from b JOIN c) AS anon", to support 
    1952        databases that can't parse a parenthesized join correctly 
    1953        (i.e. sqlite < 3.7.16). 
    1954 
    1955        """ 
    1956        cloned = {} 
    1957        column_translate = [{}] 
    1958 
    1959        def visit(element, **kw): 
    1960            if element in column_translate[-1]: 
    1961                return column_translate[-1][element] 
    1962 
    1963            elif element in cloned: 
    1964                return cloned[element] 
    1965 
    1966            newelem = cloned[element] = element._clone() 
    1967 
    1968            if ( 
    1969                newelem.is_selectable 
    1970                and newelem._is_join 
    1971                and isinstance(newelem.right, selectable.FromGrouping) 
    1972            ): 
    1973 
    1974                newelem._reset_exported() 
    1975                newelem.left = visit(newelem.left, **kw) 
    1976 
    1977                right = visit(newelem.right, **kw) 
    1978 
    1979                selectable_ = selectable.Select( 
    1980                    [right.element], use_labels=True 
    1981                ).alias() 
    1982 
    1983                for c in selectable_.c: 
    1984                    c._key_label = c.key 
    1985                    c._label = c.name 
    1986 
    1987                translate_dict = dict( 
    1988                    zip(newelem.right.element.c, selectable_.c) 
    1989                ) 
    1990 
    1991                # translating from both the old and the new 
    1992                # because different select() structures will lead us 
    1993                # to traverse differently 
    1994                translate_dict[right.element.left] = selectable_ 
    1995                translate_dict[right.element.right] = selectable_ 
    1996                translate_dict[newelem.right.element.left] = selectable_ 
    1997                translate_dict[newelem.right.element.right] = selectable_ 
    1998 
    1999                # propagate translations that we've gained 
    2000                # from nested visit(newelem.right) outwards 
    2001                # to the enclosing select here.  this happens 
    2002                # only when we have more than one level of right 
    2003                # join nesting, i.e. "a JOIN (b JOIN (c JOIN d))" 
    2004                for k, v in list(column_translate[-1].items()): 
    2005                    if v in translate_dict: 
    2006                        # remarkably, no current ORM tests (May 2013) 
    2007                        # hit this condition, only test_join_rewriting 
    2008                        # does. 
    2009                        column_translate[-1][k] = translate_dict[v] 
    2010 
    2011                column_translate[-1].update(translate_dict) 
    2012 
    2013                newelem.right = selectable_ 
    2014 
    2015                newelem.onclause = visit(newelem.onclause, **kw) 
    2016 
    2017            elif newelem._is_from_container: 
    2018                # if we hit an Alias, CompoundSelect or ScalarSelect, put a 
    2019                # marker in the stack. 
    2020                kw["transform_clue"] = "select_container" 
    2021                newelem._copy_internals(clone=visit, **kw) 
    2022            elif newelem.is_selectable and newelem._is_select: 
    2023                barrier_select = ( 
    2024                    kw.get("transform_clue", None) == "select_container" 
    2025                ) 
    2026                # if we're still descended from an 
    2027                # Alias/CompoundSelect/ScalarSelect, we're 
    2028                # in a FROM clause, so start with a new translate collection 
    2029                if barrier_select: 
    2030                    column_translate.append({}) 
    2031                kw["transform_clue"] = "inside_select" 
    2032                newelem._copy_internals(clone=visit, **kw) 
    2033                if barrier_select: 
    2034                    del column_translate[-1] 
    2035            else: 
    2036                newelem._copy_internals(clone=visit, **kw) 
    2037 
    2038            return newelem 
    2039 
    2040        return visit(select) 
    2041 
    2042    def _transform_result_map_for_nested_joins( 
    2043        self, select, transformed_select 
    2044    ): 
    2045        inner_col = dict( 
    2046            (c._key_label, c) for c in transformed_select.inner_columns 
    2047        ) 
    2048 
    2049        d = dict((inner_col[c._key_label], c) for c in select.inner_columns) 
    2050 
    2051        self._result_columns = [ 
    2052            (key, name, tuple([d.get(col, col) for col in objs]), typ) 
    2053            for key, name, objs, typ in self._result_columns 
    2054        ] 
    2055 
    2056    _default_stack_entry = util.immutabledict( 
    2057        [("correlate_froms", frozenset()), ("asfrom_froms", frozenset())] 
    2058    ) 
    2059 
    2060    def _display_froms_for_select(self, select, asfrom, lateral=False): 
    2061        # utility method to help external dialects 
    2062        # get the correct from list for a select. 
    2063        # specifically the oracle dialect needs this feature 
    2064        # right now. 
    2065        toplevel = not self.stack 
    2066        entry = self._default_stack_entry if toplevel else self.stack[-1] 
    2067 
    2068        correlate_froms = entry["correlate_froms"] 
    2069        asfrom_froms = entry["asfrom_froms"] 
    2070 
    2071        if asfrom and not lateral: 
    2072            froms = select._get_display_froms( 
    2073                explicit_correlate_froms=correlate_froms.difference( 
    2074                    asfrom_froms 
    2075                ), 
    2076                implicit_correlate_froms=(), 
    2077            ) 
    2078        else: 
    2079            froms = select._get_display_froms( 
    2080                explicit_correlate_froms=correlate_froms, 
    2081                implicit_correlate_froms=asfrom_froms, 
    2082            ) 
    2083        return froms 
    2084 
    2085    def visit_select( 
    2086        self, 
    2087        select, 
    2088        asfrom=False, 
    2089        parens=True, 
    2090        fromhints=None, 
    2091        compound_index=0, 
    2092        nested_join_translation=False, 
    2093        select_wraps_for=None, 
    2094        lateral=False, 
    2095        **kwargs 
    2096    ): 
    2097 
    2098        needs_nested_translation = ( 
    2099            select.use_labels 
    2100            and not nested_join_translation 
    2101            and not self.stack 
    2102            and not self.dialect.supports_right_nested_joins 
    2103        ) 
    2104 
    2105        if needs_nested_translation: 
    2106            transformed_select = self._transform_select_for_nested_joins( 
    2107                select 
    2108            ) 
    2109            text = self.visit_select( 
    2110                transformed_select, 
    2111                asfrom=asfrom, 
    2112                parens=parens, 
    2113                fromhints=fromhints, 
    2114                compound_index=compound_index, 
    2115                nested_join_translation=True, 
    2116                **kwargs 
    2117            ) 
    2118 
    2119        toplevel = not self.stack 
    2120        entry = self._default_stack_entry if toplevel else self.stack[-1] 
    2121 
    2122        populate_result_map = need_column_expressions = ( 
    2123            toplevel 
    2124            or entry.get("need_result_map_for_compound", False) 
    2125            or entry.get("need_result_map_for_nested", False) 
    2126        ) 
    2127 
    2128        if compound_index > 0: 
    2129            populate_result_map = False 
    2130 
    2131        # this was first proposed as part of #3372; however, it is not 
    2132        # reached in current tests and could possibly be an assertion 
    2133        # instead. 
    2134        if not populate_result_map and "add_to_result_map" in kwargs: 
    2135            del kwargs["add_to_result_map"] 
    2136 
    2137        if needs_nested_translation: 
    2138            if populate_result_map: 
    2139                self._transform_result_map_for_nested_joins( 
    2140                    select, transformed_select 
    2141                ) 
    2142            return text 
    2143 
    2144        froms = self._setup_select_stack(select, entry, asfrom, lateral) 
    2145 
    2146        column_clause_args = kwargs.copy() 
    2147        column_clause_args.update( 
    2148            {"within_label_clause": False, "within_columns_clause": False} 
    2149        ) 
    2150 
    2151        text = "SELECT "  # we're off to a good start ! 
    2152 
    2153        if select._hints: 
    2154            hint_text, byfrom = self._setup_select_hints(select) 
    2155            if hint_text: 
    2156                text += hint_text + " " 
    2157        else: 
    2158            byfrom = None 
    2159 
    2160        if select._prefixes: 
    2161            text += self._generate_prefixes(select, select._prefixes, **kwargs) 
    2162 
    2163        text += self.get_select_precolumns(select, **kwargs) 
    2164        # the actual list of columns to print in the SELECT column list. 
    2165        inner_columns = [ 
    2166            c 
    2167            for c in [ 
    2168                self._label_select_column( 
    2169                    select, 
    2170                    column, 
    2171                    populate_result_map, 
    2172                    asfrom, 
    2173                    column_clause_args, 
    2174                    name=name, 
    2175                    need_column_expressions=need_column_expressions, 
    2176                ) 
    2177                for name, column in select._columns_plus_names 
    2178            ] 
    2179            if c is not None 
    2180        ] 
    2181 
    2182        if populate_result_map and select_wraps_for is not None: 
    2183            # if this select is a compiler-generated wrapper, 
    2184            # rewrite the targeted columns in the result map 
    2185 
    2186            translate = dict( 
    2187                zip( 
    2188                    [name for (key, name) in select._columns_plus_names], 
    2189                    [ 
    2190                        name 
    2191                        for (key, name) in select_wraps_for._columns_plus_names 
    2192                    ], 
    2193                ) 
    2194            ) 
    2195 
    2196            self._result_columns = [ 
    2197                (key, name, tuple(translate.get(o, o) for o in obj), type_) 
    2198                for key, name, obj, type_ in self._result_columns 
    2199            ] 
    2200 
    2201        text = self._compose_select_body( 
    2202            text, select, inner_columns, froms, byfrom, kwargs 
    2203        ) 
    2204 
    2205        if select._statement_hints: 
    2206            per_dialect = [ 
    2207                ht 
    2208                for (dialect_name, ht) in select._statement_hints 
    2209                if dialect_name in ("*", self.dialect.name) 
    2210            ] 
    2211            if per_dialect: 
    2212                text += " " + self.get_statement_hint_text(per_dialect) 
    2213 
    2214        if self.ctes and toplevel: 
    2215            text = self._render_cte_clause() + text 
    2216 
    2217        if select._suffixes: 
    2218            text += " " + self._generate_prefixes( 
    2219                select, select._suffixes, **kwargs 
    2220            ) 
    2221 
    2222        self.stack.pop(-1) 
    2223 
    2224        if (asfrom or lateral) and parens: 
    2225            return "(" + text + ")" 
    2226        else: 
    2227            return text 
    2228 
    2229    def _setup_select_hints(self, select): 
    2230        byfrom = dict( 
    2231            [ 
    2232                ( 
    2233                    from_, 
    2234                    hinttext 
    2235                    % {"name": from_._compiler_dispatch(self, ashint=True)}, 
    2236                ) 
    2237                for (from_, dialect), hinttext in select._hints.items() 
    2238                if dialect in ("*", self.dialect.name) 
    2239            ] 
    2240        ) 
    2241        hint_text = self.get_select_hint_text(byfrom) 
    2242        return hint_text, byfrom 
    2243 
    2244    def _setup_select_stack(self, select, entry, asfrom, lateral): 
    2245        correlate_froms = entry["correlate_froms"] 
    2246        asfrom_froms = entry["asfrom_froms"] 
    2247 
    2248        if asfrom and not lateral: 
    2249            froms = select._get_display_froms( 
    2250                explicit_correlate_froms=correlate_froms.difference( 
    2251                    asfrom_froms 
    2252                ), 
    2253                implicit_correlate_froms=(), 
    2254            ) 
    2255        else: 
    2256            froms = select._get_display_froms( 
    2257                explicit_correlate_froms=correlate_froms, 
    2258                implicit_correlate_froms=asfrom_froms, 
    2259            ) 
    2260 
    2261        new_correlate_froms = set(selectable._from_objects(*froms)) 
    2262        all_correlate_froms = new_correlate_froms.union(correlate_froms) 
    2263 
    2264        new_entry = { 
    2265            "asfrom_froms": new_correlate_froms, 
    2266            "correlate_froms": all_correlate_froms, 
    2267            "selectable": select, 
    2268        } 
    2269        self.stack.append(new_entry) 
    2270 
    2271        return froms 
    2272 
    2273    def _compose_select_body( 
    2274        self, text, select, inner_columns, froms, byfrom, kwargs 
    2275    ): 
    2276        text += ", ".join(inner_columns) 
    2277 
    2278        if froms: 
    2279            text += " \nFROM " 
    2280 
    2281            if select._hints: 
    2282                text += ", ".join( 
    2283                    [ 
    2284                        f._compiler_dispatch( 
    2285                            self, asfrom=True, fromhints=byfrom, **kwargs 
    2286                        ) 
    2287                        for f in froms 
    2288                    ] 
    2289                ) 
    2290            else: 
    2291                text += ", ".join( 
    2292                    [ 
    2293                        f._compiler_dispatch(self, asfrom=True, **kwargs) 
    2294                        for f in froms 
    2295                    ] 
    2296                ) 
    2297        else: 
    2298            text += self.default_from() 
    2299 
    2300        if select._whereclause is not None: 
    2301            t = select._whereclause._compiler_dispatch(self, **kwargs) 
    2302            if t: 
    2303                text += " \nWHERE " + t 
    2304 
    2305        if select._group_by_clause.clauses: 
    2306            text += self.group_by_clause(select, **kwargs) 
    2307 
    2308        if select._having is not None: 
    2309            t = select._having._compiler_dispatch(self, **kwargs) 
    2310            if t: 
    2311                text += " \nHAVING " + t 
    2312 
    2313        if select._order_by_clause.clauses: 
    2314            text += self.order_by_clause(select, **kwargs) 
    2315 
    2316        if ( 
    2317            select._limit_clause is not None 
    2318            or select._offset_clause is not None 
    2319        ): 
    2320            text += self.limit_clause(select, **kwargs) 
    2321 
    2322        if select._for_update_arg is not None: 
    2323            text += self.for_update_clause(select, **kwargs) 
    2324 
    2325        return text 
    2326 
    2327    def _generate_prefixes(self, stmt, prefixes, **kw): 
    2328        clause = " ".join( 
    2329            prefix._compiler_dispatch(self, **kw) 
    2330            for prefix, dialect_name in prefixes 
    2331            if dialect_name is None or dialect_name == self.dialect.name 
    2332        ) 
    2333        if clause: 
    2334            clause += " " 
    2335        return clause 
    2336 
    2337    def _render_cte_clause(self): 
    2338        if self.positional: 
    2339            self.positiontup = ( 
    2340                sum([self.cte_positional[cte] for cte in self.ctes], []) 
    2341                + self.positiontup 
    2342            ) 
    2343        cte_text = self.get_cte_preamble(self.ctes_recursive) + " " 
    2344        cte_text += ", \n".join([txt for txt in self.ctes.values()]) 
    2345        cte_text += "\n " 
    2346        return cte_text 
    2347 
    2348    def get_cte_preamble(self, recursive): 
    2349        if recursive: 
    2350            return "WITH RECURSIVE" 
    2351        else: 
    2352            return "WITH" 
    2353 
    2354    def get_select_precolumns(self, select, **kw): 
    2355        """Called when building a ``SELECT`` statement, position is just 
    2356        before column list. 
    2357 
    2358        """ 
    2359        return select._distinct and "DISTINCT " or "" 
    2360 
    2361    def group_by_clause(self, select, **kw): 
    2362        """allow dialects to customize how GROUP BY is rendered.""" 
    2363 
    2364        group_by = select._group_by_clause._compiler_dispatch(self, **kw) 
    2365        if group_by: 
    2366            return " GROUP BY " + group_by 
    2367        else: 
    2368            return "" 
    2369 
    2370    def order_by_clause(self, select, **kw): 
    2371        """allow dialects to customize how ORDER BY is rendered.""" 
    2372 
    2373        order_by = select._order_by_clause._compiler_dispatch(self, **kw) 
    2374        if order_by: 
    2375            return " ORDER BY " + order_by 
    2376        else: 
    2377            return "" 
    2378 
    2379    def for_update_clause(self, select, **kw): 
    2380        return " FOR UPDATE" 
    2381 
    2382    def returning_clause(self, stmt, returning_cols): 
    2383        raise exc.CompileError( 
    2384            "RETURNING is not supported by this " 
    2385            "dialect's statement compiler." 
    2386        ) 
    2387 
    2388    def limit_clause(self, select, **kw): 
    2389        text = "" 
    2390        if select._limit_clause is not None: 
    2391            text += "\n LIMIT " + self.process(select._limit_clause, **kw) 
    2392        if select._offset_clause is not None: 
    2393            if select._limit_clause is None: 
    2394                text += "\n LIMIT -1" 
    2395            text += " OFFSET " + self.process(select._offset_clause, **kw) 
    2396        return text 
    2397 
    2398    def visit_table( 
    2399        self, 
    2400        table, 
    2401        asfrom=False, 
    2402        iscrud=False, 
    2403        ashint=False, 
    2404        fromhints=None, 
    2405        use_schema=True, 
    2406        **kwargs 
    2407    ): 
    2408        if asfrom or ashint: 
    2409            effective_schema = self.preparer.schema_for_object(table) 
    2410 
    2411            if use_schema and effective_schema: 
    2412                ret = ( 
    2413                    self.preparer.quote_schema(effective_schema) 
    2414                    + "." 
    2415                    + self.preparer.quote(table.name) 
    2416                ) 
    2417            else: 
    2418                ret = self.preparer.quote(table.name) 
    2419            if fromhints and table in fromhints: 
    2420                ret = self.format_from_hint_text( 
    2421                    ret, table, fromhints[table], iscrud 
    2422                ) 
    2423            return ret 
    2424        else: 
    2425            return "" 
    2426 
    2427    def visit_join(self, join, asfrom=False, **kwargs): 
    2428        if join.full: 
    2429            join_type = " FULL OUTER JOIN " 
    2430        elif join.isouter: 
    2431            join_type = " LEFT OUTER JOIN " 
    2432        else: 
    2433            join_type = " JOIN " 
    2434        return ( 
    2435            join.left._compiler_dispatch(self, asfrom=True, **kwargs) 
    2436            + join_type 
    2437            + join.right._compiler_dispatch(self, asfrom=True, **kwargs) 
    2438            + " ON " 
    2439            + join.onclause._compiler_dispatch(self, **kwargs) 
    2440        ) 
    2441 
    2442    def _setup_crud_hints(self, stmt, table_text): 
    2443        dialect_hints = dict( 
    2444            [ 
    2445                (table, hint_text) 
    2446                for (table, dialect), hint_text in stmt._hints.items() 
    2447                if dialect in ("*", self.dialect.name) 
    2448            ] 
    2449        ) 
    2450        if stmt.table in dialect_hints: 
    2451            table_text = self.format_from_hint_text( 
    2452                table_text, stmt.table, dialect_hints[stmt.table], True 
    2453            ) 
    2454        return dialect_hints, table_text 
    2455 
    2456    def visit_insert(self, insert_stmt, asfrom=False, **kw): 
    2457        toplevel = not self.stack 
    2458 
    2459        self.stack.append( 
    2460            { 
    2461                "correlate_froms": set(), 
    2462                "asfrom_froms": set(), 
    2463                "selectable": insert_stmt, 
    2464            } 
    2465        ) 
    2466 
    2467        crud_params = crud._setup_crud_params( 
    2468            self, insert_stmt, crud.ISINSERT, **kw 
    2469        ) 
    2470 
    2471        if ( 
    2472            not crud_params 
    2473            and not self.dialect.supports_default_values 
    2474            and not self.dialect.supports_empty_insert 
    2475        ): 
    2476            raise exc.CompileError( 
    2477                "The '%s' dialect with current database " 
    2478                "version settings does not support empty " 
    2479                "inserts." % self.dialect.name 
    2480            ) 
    2481 
    2482        if insert_stmt._has_multi_parameters: 
    2483            if not self.dialect.supports_multivalues_insert: 
    2484                raise exc.CompileError( 
    2485                    "The '%s' dialect with current database " 
    2486                    "version settings does not support " 
    2487                    "in-place multirow inserts." % self.dialect.name 
    2488                ) 
    2489            crud_params_single = crud_params[0] 
    2490        else: 
    2491            crud_params_single = crud_params 
    2492 
    2493        preparer = self.preparer 
    2494        supports_default_values = self.dialect.supports_default_values 
    2495 
    2496        text = "INSERT " 
    2497 
    2498        if insert_stmt._prefixes: 
    2499            text += self._generate_prefixes( 
    2500                insert_stmt, insert_stmt._prefixes, **kw 
    2501            ) 
    2502 
    2503        text += "INTO " 
    2504        table_text = preparer.format_table(insert_stmt.table) 
    2505 
    2506        if insert_stmt._hints: 
    2507            _, table_text = self._setup_crud_hints(insert_stmt, table_text) 
    2508 
    2509        text += table_text 
    2510 
    2511        if crud_params_single or not supports_default_values: 
    2512            text += " (%s)" % ", ".join( 
    2513                [preparer.format_column(c[0]) for c in crud_params_single] 
    2514            ) 
    2515 
    2516        if self.returning or insert_stmt._returning: 
    2517            returning_clause = self.returning_clause( 
    2518                insert_stmt, self.returning or insert_stmt._returning 
    2519            ) 
    2520 
    2521            if self.returning_precedes_values: 
    2522                text += " " + returning_clause 
    2523        else: 
    2524            returning_clause = None 
    2525 
    2526        if insert_stmt.select is not None: 
    2527            select_text = self.process(self._insert_from_select, **kw) 
    2528 
    2529            if self.ctes and toplevel and self.dialect.cte_follows_insert: 
    2530                text += " %s%s" % (self._render_cte_clause(), select_text) 
    2531            else: 
    2532                text += " %s" % select_text 
    2533        elif not crud_params and supports_default_values: 
    2534            text += " DEFAULT VALUES" 
    2535        elif insert_stmt._has_multi_parameters: 
    2536            text += " VALUES %s" % ( 
    2537                ", ".join( 
    2538                    "(%s)" % (", ".join(c[1] for c in crud_param_set)) 
    2539                    for crud_param_set in crud_params 
    2540                ) 
    2541            ) 
    2542        else: 
    2543            insert_single_values_expr = ", ".join([c[1] for c in crud_params]) 
    2544            text += " VALUES (%s)" % insert_single_values_expr 
    2545            if toplevel: 
    2546                self.insert_single_values_expr = insert_single_values_expr 
    2547 
    2548        if insert_stmt._post_values_clause is not None: 
    2549            post_values_clause = self.process( 
    2550                insert_stmt._post_values_clause, **kw 
    2551            ) 
    2552            if post_values_clause: 
    2553                text += " " + post_values_clause 
    2554 
    2555        if returning_clause and not self.returning_precedes_values: 
    2556            text += " " + returning_clause 
    2557 
    2558        if self.ctes and toplevel and not self.dialect.cte_follows_insert: 
    2559            text = self._render_cte_clause() + text 
    2560 
    2561        self.stack.pop(-1) 
    2562 
    2563        if asfrom: 
    2564            return "(" + text + ")" 
    2565        else: 
    2566            return text 
    2567 
    2568    def update_limit_clause(self, update_stmt): 
    2569        """Provide a hook for MySQL to add LIMIT to the UPDATE""" 
    2570        return None 
    2571 
    2572    def update_tables_clause(self, update_stmt, from_table, extra_froms, **kw): 
    2573        """Provide a hook to override the initial table clause 
    2574        in an UPDATE statement. 
    2575 
    2576        MySQL overrides this. 
    2577 
    2578        """ 
    2579        kw["asfrom"] = True 
    2580        return from_table._compiler_dispatch(self, iscrud=True, **kw) 
    2581 
    2582    def update_from_clause( 
    2583        self, update_stmt, from_table, extra_froms, from_hints, **kw 
    2584    ): 
    2585        """Provide a hook to override the generation of an 
    2586        UPDATE..FROM clause. 
    2587 
    2588        MySQL and MSSQL override this. 
    2589 
    2590        """ 
    2591        raise NotImplementedError( 
    2592            "This backend does not support multiple-table " 
    2593            "criteria within UPDATE" 
    2594        ) 
    2595 
    2596    def visit_update(self, update_stmt, asfrom=False, **kw): 
    2597        toplevel = not self.stack 
    2598 
    2599        extra_froms = update_stmt._extra_froms 
    2600        is_multitable = bool(extra_froms) 
    2601 
    2602        if is_multitable: 
    2603            # main table might be a JOIN 
    2604            main_froms = set(selectable._from_objects(update_stmt.table)) 
    2605            render_extra_froms = [ 
    2606                f for f in extra_froms if f not in main_froms 
    2607            ] 
    2608            correlate_froms = main_froms.union(extra_froms) 
    2609        else: 
    2610            render_extra_froms = [] 
    2611            correlate_froms = {update_stmt.table} 
    2612 
    2613        self.stack.append( 
    2614            { 
    2615                "correlate_froms": correlate_froms, 
    2616                "asfrom_froms": correlate_froms, 
    2617                "selectable": update_stmt, 
    2618            } 
    2619        ) 
    2620 
    2621        text = "UPDATE " 
    2622 
    2623        if update_stmt._prefixes: 
    2624            text += self._generate_prefixes( 
    2625                update_stmt, update_stmt._prefixes, **kw 
    2626            ) 
    2627 
    2628        table_text = self.update_tables_clause( 
    2629            update_stmt, update_stmt.table, render_extra_froms, **kw 
    2630        ) 
    2631        crud_params = crud._setup_crud_params( 
    2632            self, update_stmt, crud.ISUPDATE, **kw 
    2633        ) 
    2634 
    2635        if update_stmt._hints: 
    2636            dialect_hints, table_text = self._setup_crud_hints( 
    2637                update_stmt, table_text 
    2638            ) 
    2639        else: 
    2640            dialect_hints = None 
    2641 
    2642        text += table_text 
    2643 
    2644        text += " SET " 
    2645        include_table = ( 
    2646            is_multitable and self.render_table_with_column_in_update_from 
    2647        ) 
    2648        text += ", ".join( 
    2649            c[0]._compiler_dispatch(self, include_table=include_table) 
    2650            + "=" 
    2651            + c[1] 
    2652            for c in crud_params 
    2653        ) 
    2654 
    2655        if self.returning or update_stmt._returning: 
    2656            if self.returning_precedes_values: 
    2657                text += " " + self.returning_clause( 
    2658                    update_stmt, self.returning or update_stmt._returning 
    2659                ) 
    2660 
    2661        if extra_froms: 
    2662            extra_from_text = self.update_from_clause( 
    2663                update_stmt, 
    2664                update_stmt.table, 
    2665                render_extra_froms, 
    2666                dialect_hints, 
    2667                **kw 
    2668            ) 
    2669            if extra_from_text: 
    2670                text += " " + extra_from_text 
    2671 
    2672        if update_stmt._whereclause is not None: 
    2673            t = self.process(update_stmt._whereclause, **kw) 
    2674            if t: 
    2675                text += " WHERE " + t 
    2676 
    2677        limit_clause = self.update_limit_clause(update_stmt) 
    2678        if limit_clause: 
    2679            text += " " + limit_clause 
    2680 
    2681        if ( 
    2682            self.returning or update_stmt._returning 
    2683        ) and not self.returning_precedes_values: 
    2684            text += " " + self.returning_clause( 
    2685                update_stmt, self.returning or update_stmt._returning 
    2686            ) 
    2687 
    2688        if self.ctes and toplevel: 
    2689            text = self._render_cte_clause() + text 
    2690 
    2691        self.stack.pop(-1) 
    2692 
    2693        if asfrom: 
    2694            return "(" + text + ")" 
    2695        else: 
    2696            return text 
    2697 
    2698    @util.memoized_property 
    2699    def _key_getters_for_crud_column(self): 
    2700        return crud._key_getters_for_crud_column(self, self.statement) 
    2701 
    2702    def delete_extra_from_clause( 
    2703        self, update_stmt, from_table, extra_froms, from_hints, **kw 
    2704    ): 
    2705        """Provide a hook to override the generation of an 
    2706        DELETE..FROM clause. 
    2707 
    2708        This can be used to implement DELETE..USING for example. 
    2709 
    2710        MySQL and MSSQL override this. 
    2711 
    2712        """ 
    2713        raise NotImplementedError( 
    2714            "This backend does not support multiple-table " 
    2715            "criteria within DELETE" 
    2716        ) 
    2717 
    2718    def delete_table_clause(self, delete_stmt, from_table, extra_froms): 
    2719        return from_table._compiler_dispatch(self, asfrom=True, iscrud=True) 
    2720 
    2721    def visit_delete(self, delete_stmt, asfrom=False, **kw): 
    2722        toplevel = not self.stack 
    2723 
    2724        crud._setup_crud_params(self, delete_stmt, crud.ISDELETE, **kw) 
    2725 
    2726        extra_froms = delete_stmt._extra_froms 
    2727 
    2728        correlate_froms = {delete_stmt.table}.union(extra_froms) 
    2729        self.stack.append( 
    2730            { 
    2731                "correlate_froms": correlate_froms, 
    2732                "asfrom_froms": correlate_froms, 
    2733                "selectable": delete_stmt, 
    2734            } 
    2735        ) 
    2736 
    2737        text = "DELETE " 
    2738 
    2739        if delete_stmt._prefixes: 
    2740            text += self._generate_prefixes( 
    2741                delete_stmt, delete_stmt._prefixes, **kw 
    2742            ) 
    2743 
    2744        text += "FROM " 
    2745        table_text = self.delete_table_clause( 
    2746            delete_stmt, delete_stmt.table, extra_froms 
    2747        ) 
    2748 
    2749        if delete_stmt._hints: 
    2750            dialect_hints, table_text = self._setup_crud_hints( 
    2751                delete_stmt, table_text 
    2752            ) 
    2753        else: 
    2754            dialect_hints = None 
    2755 
    2756        text += table_text 
    2757 
    2758        if delete_stmt._returning: 
    2759            if self.returning_precedes_values: 
    2760                text += " " + self.returning_clause( 
    2761                    delete_stmt, delete_stmt._returning 
    2762                ) 
    2763 
    2764        if extra_froms: 
    2765            extra_from_text = self.delete_extra_from_clause( 
    2766                delete_stmt, 
    2767                delete_stmt.table, 
    2768                extra_froms, 
    2769                dialect_hints, 
    2770                **kw 
    2771            ) 
    2772            if extra_from_text: 
    2773                text += " " + extra_from_text 
    2774 
    2775        if delete_stmt._whereclause is not None: 
    2776            t = delete_stmt._whereclause._compiler_dispatch(self, **kw) 
    2777            if t: 
    2778                text += " WHERE " + t 
    2779 
    2780        if delete_stmt._returning and not self.returning_precedes_values: 
    2781            text += " " + self.returning_clause( 
    2782                delete_stmt, delete_stmt._returning 
    2783            ) 
    2784 
    2785        if self.ctes and toplevel: 
    2786            text = self._render_cte_clause() + text 
    2787 
    2788        self.stack.pop(-1) 
    2789 
    2790        if asfrom: 
    2791            return "(" + text + ")" 
    2792        else: 
    2793            return text 
    2794 
    2795    def visit_savepoint(self, savepoint_stmt): 
    2796        return "SAVEPOINT %s" % self.preparer.format_savepoint(savepoint_stmt) 
    2797 
    2798    def visit_rollback_to_savepoint(self, savepoint_stmt): 
    2799        return "ROLLBACK TO SAVEPOINT %s" % self.preparer.format_savepoint( 
    2800            savepoint_stmt 
    2801        ) 
    2802 
    2803    def visit_release_savepoint(self, savepoint_stmt): 
    2804        return "RELEASE SAVEPOINT %s" % self.preparer.format_savepoint( 
    2805            savepoint_stmt 
    2806        ) 
    2807 
    2808 
    2809class StrSQLCompiler(SQLCompiler): 
    2810    """A :class:`.SQLCompiler` subclass which allows a small selection 
    2811    of non-standard SQL features to render into a string value. 
    2812 
    2813    The :class:`.StrSQLCompiler` is invoked whenever a Core expression 
    2814    element is directly stringified without calling upon the 
    2815    :meth:`_expression.ClauseElement.compile` method. 
    2816    It can render a limited set 
    2817    of non-standard SQL constructs to assist in basic stringification, 
    2818    however for more substantial custom or dialect-specific SQL constructs, 
    2819    it will be necessary to make use of 
    2820    :meth:`_expression.ClauseElement.compile` 
    2821    directly. 
    2822 
    2823    .. seealso:: 
    2824 
    2825        :ref:`faq_sql_expression_string` 
    2826 
    2827    """ 
    2828 
    2829    def _fallback_column_name(self, column): 
    2830        return "<name unknown>" 
    2831 
    2832    def visit_getitem_binary(self, binary, operator, **kw): 
    2833        return "%s[%s]" % ( 
    2834            self.process(binary.left, **kw), 
    2835            self.process(binary.right, **kw), 
    2836        ) 
    2837 
    2838    def visit_json_getitem_op_binary(self, binary, operator, **kw): 
    2839        return self.visit_getitem_binary(binary, operator, **kw) 
    2840 
    2841    def visit_json_path_getitem_op_binary(self, binary, operator, **kw): 
    2842        return self.visit_getitem_binary(binary, operator, **kw) 
    2843 
    2844    def visit_sequence(self, seq, **kw): 
    2845        return "<next sequence value: %s>" % self.preparer.format_sequence(seq) 
    2846 
    2847    def returning_clause(self, stmt, returning_cols): 
    2848        columns = [ 
    2849            self._label_select_column(None, c, True, False, {}) 
    2850            for c in elements._select_iterables(returning_cols) 
    2851        ] 
    2852 
    2853        return "RETURNING " + ", ".join(columns) 
    2854 
    2855    def update_from_clause( 
    2856        self, update_stmt, from_table, extra_froms, from_hints, **kw 
    2857    ): 
    2858        return "FROM " + ", ".join( 
    2859            t._compiler_dispatch(self, asfrom=True, fromhints=from_hints, **kw) 
    2860            for t in extra_froms 
    2861        ) 
    2862 
    2863    def delete_extra_from_clause( 
    2864        self, update_stmt, from_table, extra_froms, from_hints, **kw 
    2865    ): 
    2866        return ", " + ", ".join( 
    2867            t._compiler_dispatch(self, asfrom=True, fromhints=from_hints, **kw) 
    2868            for t in extra_froms 
    2869        ) 
    2870 
    2871    def get_from_hint_text(self, table, text): 
    2872        return "[%s]" % text 
    2873 
    2874 
    2875class DDLCompiler(Compiled): 
    2876    @util.memoized_property 
    2877    def sql_compiler(self): 
    2878        return self.dialect.statement_compiler( 
    2879            self.dialect, None, schema_translate_map=self.schema_translate_map 
    2880        ) 
    2881 
    2882    @util.memoized_property 
    2883    def type_compiler(self): 
    2884        return self.dialect.type_compiler 
    2885 
    2886    def construct_params(self, params=None): 
    2887        return None 
    2888 
    2889    def visit_ddl(self, ddl, **kwargs): 
    2890        # table events can substitute table and schema name 
    2891        context = ddl.context 
    2892        if isinstance(ddl.target, schema.Table): 
    2893            context = context.copy() 
    2894 
    2895            preparer = self.preparer 
    2896            path = preparer.format_table_seq(ddl.target) 
    2897            if len(path) == 1: 
    2898                table, sch = path[0], "" 
    2899            else: 
    2900                table, sch = path[-1], path[0] 
    2901 
    2902            context.setdefault("table", table) 
    2903            context.setdefault("schema", sch) 
    2904            context.setdefault("fullname", preparer.format_table(ddl.target)) 
    2905 
    2906        return self.sql_compiler.post_process_text(ddl.statement % context) 
    2907 
    2908    def visit_create_schema(self, create): 
    2909        schema = self.preparer.format_schema(create.element) 
    2910        return "CREATE SCHEMA " + schema 
    2911 
    2912    def visit_drop_schema(self, drop): 
    2913        schema = self.preparer.format_schema(drop.element) 
    2914        text = "DROP SCHEMA " + schema 
    2915        if drop.cascade: 
    2916            text += " CASCADE" 
    2917        return text 
    2918 
    2919    def visit_create_table(self, create): 
    2920        table = create.element 
    2921        preparer = self.preparer 
    2922 
    2923        text = "\nCREATE " 
    2924        if table._prefixes: 
    2925            text += " ".join(table._prefixes) + " " 
    2926        text += "TABLE " + preparer.format_table(table) + " " 
    2927 
    2928        create_table_suffix = self.create_table_suffix(table) 
    2929        if create_table_suffix: 
    2930            text += create_table_suffix + " " 
    2931 
    2932        text += "(" 
    2933 
    2934        separator = "\n" 
    2935 
    2936        # if only one primary key, specify it along with the column 
    2937        first_pk = False 
    2938        for create_column in create.columns: 
    2939            column = create_column.element 
    2940            try: 
    2941                processed = self.process( 
    2942                    create_column, first_pk=column.primary_key and not first_pk 
    2943                ) 
    2944                if processed is not None: 
    2945                    text += separator 
    2946                    separator = ", \n" 
    2947                    text += "\t" + processed 
    2948                if column.primary_key: 
    2949                    first_pk = True 
    2950            except exc.CompileError as ce: 
    2951                util.raise_( 
    2952                    exc.CompileError( 
    2953                        util.u("(in table '%s', column '%s'): %s") 
    2954                        % (table.description, column.name, ce.args[0]) 
    2955                    ), 
    2956                    from_=ce, 
    2957                ) 
    2958 
    2959        const = self.create_table_constraints( 
    2960            table, 
    2961            _include_foreign_key_constraints=create.include_foreign_key_constraints,  # noqa 
    2962        ) 
    2963        if const: 
    2964            text += separator + "\t" + const 
    2965 
    2966        text += "\n)%s\n\n" % self.post_create_table(table) 
    2967        return text 
    2968 
    2969    def visit_create_column(self, create, first_pk=False): 
    2970        column = create.element 
    2971 
    2972        if column.system: 
    2973            return None 
    2974 
    2975        text = self.get_column_specification(column, first_pk=first_pk) 
    2976        const = " ".join( 
    2977            self.process(constraint) for constraint in column.constraints 
    2978        ) 
    2979        if const: 
    2980            text += " " + const 
    2981 
    2982        return text 
    2983 
    2984    def create_table_constraints( 
    2985        self, table, _include_foreign_key_constraints=None 
    2986    ): 
    2987 
    2988        # On some DB order is significant: visit PK first, then the 
    2989        # other constraints (engine.ReflectionTest.testbasic failed on FB2) 
    2990        constraints = [] 
    2991        if table.primary_key: 
    2992            constraints.append(table.primary_key) 
    2993 
    2994        all_fkcs = table.foreign_key_constraints 
    2995        if _include_foreign_key_constraints is not None: 
    2996            omit_fkcs = all_fkcs.difference(_include_foreign_key_constraints) 
    2997        else: 
    2998            omit_fkcs = set() 
    2999 
    3000        constraints.extend( 
    3001            [ 
    3002                c 
    3003                for c in table._sorted_constraints 
    3004                if c is not table.primary_key and c not in omit_fkcs 
    3005            ] 
    3006        ) 
    3007 
    3008        return ", \n\t".join( 
    3009            p 
    3010            for p in ( 
    3011                self.process(constraint) 
    3012                for constraint in constraints 
    3013                if ( 
    3014                    constraint._create_rule is None 
    3015                    or constraint._create_rule(self) 
    3016                ) 
    3017                and ( 
    3018                    not self.dialect.supports_alter 
    3019                    or not getattr(constraint, "use_alter", False) 
    3020                ) 
    3021            ) 
    3022            if p is not None 
    3023        ) 
    3024 
    3025    def visit_drop_table(self, drop): 
    3026        return "\nDROP TABLE " + self.preparer.format_table(drop.element) 
    3027 
    3028    def visit_drop_view(self, drop): 
    3029        return "\nDROP VIEW " + self.preparer.format_table(drop.element) 
    3030 
    3031    def _verify_index_table(self, index): 
    3032        if index.table is None: 
    3033            raise exc.CompileError( 
    3034                "Index '%s' is not associated " "with any table." % index.name 
    3035            ) 
    3036 
    3037    def visit_create_index( 
    3038        self, create, include_schema=False, include_table_schema=True 
    3039    ): 
    3040        index = create.element 
    3041        self._verify_index_table(index) 
    3042        preparer = self.preparer 
    3043        text = "CREATE " 
    3044        if index.unique: 
    3045            text += "UNIQUE " 
    3046        if index.name is None: 
    3047            raise exc.CompileError( 
    3048                "CREATE INDEX requires that the index have a name" 
    3049            ) 
    3050        text += "INDEX %s ON %s (%s)" % ( 
    3051            self._prepared_index_name(index, include_schema=include_schema), 
    3052            preparer.format_table( 
    3053                index.table, use_schema=include_table_schema 
    3054            ), 
    3055            ", ".join( 
    3056                self.sql_compiler.process( 
    3057                    expr, include_table=False, literal_binds=True 
    3058                ) 
    3059                for expr in index.expressions 
    3060            ), 
    3061        ) 
    3062        return text 
    3063 
    3064    def visit_drop_index(self, drop): 
    3065        index = drop.element 
    3066 
    3067        if index.name is None: 
    3068            raise exc.CompileError( 
    3069                "DROP INDEX requires that the index have a name" 
    3070            ) 
    3071        return "\nDROP INDEX " + self._prepared_index_name( 
    3072            index, include_schema=True 
    3073        ) 
    3074 
    3075    def _prepared_index_name(self, index, include_schema=False): 
    3076        if index.table is not None: 
    3077            effective_schema = self.preparer.schema_for_object(index.table) 
    3078        else: 
    3079            effective_schema = None 
    3080        if include_schema and effective_schema: 
    3081            schema_name = self.preparer.quote_schema(effective_schema) 
    3082        else: 
    3083            schema_name = None 
    3084 
    3085        index_name = self.preparer.format_index(index) 
    3086 
    3087        if schema_name: 
    3088            index_name = schema_name + "." + index_name 
    3089        return index_name 
    3090 
    3091    def visit_add_constraint(self, create): 
    3092        return "ALTER TABLE %s ADD %s" % ( 
    3093            self.preparer.format_table(create.element.table), 
    3094            self.process(create.element), 
    3095        ) 
    3096 
    3097    def visit_set_table_comment(self, create): 
    3098        return "COMMENT ON TABLE %s IS %s" % ( 
    3099            self.preparer.format_table(create.element), 
    3100            self.sql_compiler.render_literal_value( 
    3101                create.element.comment, sqltypes.String() 
    3102            ), 
    3103        ) 
    3104 
    3105    def visit_drop_table_comment(self, drop): 
    3106        return "COMMENT ON TABLE %s IS NULL" % self.preparer.format_table( 
    3107            drop.element 
    3108        ) 
    3109 
    3110    def visit_set_column_comment(self, create): 
    3111        return "COMMENT ON COLUMN %s IS %s" % ( 
    3112            self.preparer.format_column( 
    3113                create.element, use_table=True, use_schema=True 
    3114            ), 
    3115            self.sql_compiler.render_literal_value( 
    3116                create.element.comment, sqltypes.String() 
    3117            ), 
    3118        ) 
    3119 
    3120    def visit_drop_column_comment(self, drop): 
    3121        return "COMMENT ON COLUMN %s IS NULL" % self.preparer.format_column( 
    3122            drop.element, use_table=True 
    3123        ) 
    3124 
    3125    def visit_create_sequence(self, create): 
    3126        text = "CREATE SEQUENCE %s" % self.preparer.format_sequence( 
    3127            create.element 
    3128        ) 
    3129        if create.element.increment is not None: 
    3130            text += " INCREMENT BY %d" % create.element.increment 
    3131        if create.element.start is not None: 
    3132            text += " START WITH %d" % create.element.start 
    3133        if create.element.minvalue is not None: 
    3134            text += " MINVALUE %d" % create.element.minvalue 
    3135        if create.element.maxvalue is not None: 
    3136            text += " MAXVALUE %d" % create.element.maxvalue 
    3137        if create.element.nominvalue is not None: 
    3138            text += " NO MINVALUE" 
    3139        if create.element.nomaxvalue is not None: 
    3140            text += " NO MAXVALUE" 
    3141        if create.element.cache is not None: 
    3142            text += " CACHE %d" % create.element.cache 
    3143        if create.element.order is True: 
    3144            text += " ORDER" 
    3145        if create.element.cycle is not None: 
    3146            text += " CYCLE" 
    3147        return text 
    3148 
    3149    def visit_drop_sequence(self, drop): 
    3150        return "DROP SEQUENCE %s" % self.preparer.format_sequence(drop.element) 
    3151 
    3152    def visit_drop_constraint(self, drop): 
    3153        constraint = drop.element 
    3154        if constraint.name is not None: 
    3155            formatted_name = self.preparer.format_constraint(constraint) 
    3156        else: 
    3157            formatted_name = None 
    3158 
    3159        if formatted_name is None: 
    3160            raise exc.CompileError( 
    3161                "Can't emit DROP CONSTRAINT for constraint %r; " 
    3162                "it has no name" % drop.element 
    3163            ) 
    3164        return "ALTER TABLE %s DROP CONSTRAINT %s%s" % ( 
    3165            self.preparer.format_table(drop.element.table), 
    3166            formatted_name, 
    3167            drop.cascade and " CASCADE" or "", 
    3168        ) 
    3169 
    3170    def get_column_specification(self, column, **kwargs): 
    3171        colspec = ( 
    3172            self.preparer.format_column(column) 
    3173            + " " 
    3174            + self.dialect.type_compiler.process( 
    3175                column.type, type_expression=column 
    3176            ) 
    3177        ) 
    3178        default = self.get_column_default_string(column) 
    3179        if default is not None: 
    3180            colspec += " DEFAULT " + default 
    3181 
    3182        if column.computed is not None: 
    3183            colspec += " " + self.process(column.computed) 
    3184 
    3185        if not column.nullable: 
    3186            colspec += " NOT NULL" 
    3187        return colspec 
    3188 
    3189    def create_table_suffix(self, table): 
    3190        return "" 
    3191 
    3192    def post_create_table(self, table): 
    3193        return "" 
    3194 
    3195    def get_column_default_string(self, column): 
    3196        if isinstance(column.server_default, schema.DefaultClause): 
    3197            if isinstance(column.server_default.arg, util.string_types): 
    3198                return self.sql_compiler.render_literal_value( 
    3199                    column.server_default.arg, sqltypes.STRINGTYPE 
    3200                ) 
    3201            else: 
    3202                return self.sql_compiler.process( 
    3203                    column.server_default.arg, literal_binds=True 
    3204                ) 
    3205        else: 
    3206            return None 
    3207 
    3208    def visit_check_constraint(self, constraint): 
    3209        text = "" 
    3210        if constraint.name is not None: 
    3211            formatted_name = self.preparer.format_constraint(constraint) 
    3212            if formatted_name is not None: 
    3213                text += "CONSTRAINT %s " % formatted_name 
    3214        text += "CHECK (%s)" % self.sql_compiler.process( 
    3215            constraint.sqltext, include_table=False, literal_binds=True 
    3216        ) 
    3217        text += self.define_constraint_deferrability(constraint) 
    3218        return text 
    3219 
    3220    def visit_column_check_constraint(self, constraint): 
    3221        text = "" 
    3222        if constraint.name is not None: 
    3223            formatted_name = self.preparer.format_constraint(constraint) 
    3224            if formatted_name is not None: 
    3225                text += "CONSTRAINT %s " % formatted_name 
    3226        text += "CHECK (%s)" % self.sql_compiler.process( 
    3227            constraint.sqltext, include_table=False, literal_binds=True 
    3228        ) 
    3229        text += self.define_constraint_deferrability(constraint) 
    3230        return text 
    3231 
    3232    def visit_primary_key_constraint(self, constraint): 
    3233        if len(constraint) == 0: 
    3234            return "" 
    3235        text = "" 
    3236        if constraint.name is not None: 
    3237            formatted_name = self.preparer.format_constraint(constraint) 
    3238            if formatted_name is not None: 
    3239                text += "CONSTRAINT %s " % formatted_name 
    3240        text += "PRIMARY KEY " 
    3241        text += "(%s)" % ", ".join( 
    3242            self.preparer.quote(c.name) 
    3243            for c in ( 
    3244                constraint.columns_autoinc_first 
    3245                if constraint._implicit_generated 
    3246                else constraint.columns 
    3247            ) 
    3248        ) 
    3249        text += self.define_constraint_deferrability(constraint) 
    3250        return text 
    3251 
    3252    def visit_foreign_key_constraint(self, constraint): 
    3253        preparer = self.preparer 
    3254        text = "" 
    3255        if constraint.name is not None: 
    3256            formatted_name = self.preparer.format_constraint(constraint) 
    3257            if formatted_name is not None: 
    3258                text += "CONSTRAINT %s " % formatted_name 
    3259        remote_table = list(constraint.elements)[0].column.table 
    3260        text += "FOREIGN KEY(%s) REFERENCES %s (%s)" % ( 
    3261            ", ".join( 
    3262                preparer.quote(f.parent.name) for f in constraint.elements 
    3263            ), 
    3264            self.define_constraint_remote_table( 
    3265                constraint, remote_table, preparer 
    3266            ), 
    3267            ", ".join( 
    3268                preparer.quote(f.column.name) for f in constraint.elements 
    3269            ), 
    3270        ) 
    3271        text += self.define_constraint_match(constraint) 
    3272        text += self.define_constraint_cascades(constraint) 
    3273        text += self.define_constraint_deferrability(constraint) 
    3274        return text 
    3275 
    3276    def define_constraint_remote_table(self, constraint, table, preparer): 
    3277        """Format the remote table clause of a CREATE CONSTRAINT clause.""" 
    3278 
    3279        return preparer.format_table(table) 
    3280 
    3281    def visit_unique_constraint(self, constraint): 
    3282        if len(constraint) == 0: 
    3283            return "" 
    3284        text = "" 
    3285        if constraint.name is not None: 
    3286            formatted_name = self.preparer.format_constraint(constraint) 
    3287            if formatted_name is not None: 
    3288                text += "CONSTRAINT %s " % formatted_name 
    3289        text += "UNIQUE (%s)" % ( 
    3290            ", ".join(self.preparer.quote(c.name) for c in constraint) 
    3291        ) 
    3292        text += self.define_constraint_deferrability(constraint) 
    3293        return text 
    3294 
    3295    def define_constraint_cascades(self, constraint): 
    3296        text = "" 
    3297        if constraint.ondelete is not None: 
    3298            text += " ON DELETE %s" % self.preparer.validate_sql_phrase( 
    3299                constraint.ondelete, FK_ON_DELETE 
    3300            ) 
    3301        if constraint.onupdate is not None: 
    3302            text += " ON UPDATE %s" % self.preparer.validate_sql_phrase( 
    3303                constraint.onupdate, FK_ON_UPDATE 
    3304            ) 
    3305        return text 
    3306 
    3307    def define_constraint_deferrability(self, constraint): 
    3308        text = "" 
    3309        if constraint.deferrable is not None: 
    3310            if constraint.deferrable: 
    3311                text += " DEFERRABLE" 
    3312            else: 
    3313                text += " NOT DEFERRABLE" 
    3314        if constraint.initially is not None: 
    3315            text += " INITIALLY %s" % self.preparer.validate_sql_phrase( 
    3316                constraint.initially, FK_INITIALLY 
    3317            ) 
    3318        return text 
    3319 
    3320    def define_constraint_match(self, constraint): 
    3321        text = "" 
    3322        if constraint.match is not None: 
    3323            text += " MATCH %s" % constraint.match 
    3324        return text 
    3325 
    3326    def visit_computed_column(self, generated): 
    3327        text = "GENERATED ALWAYS AS (%s)" % self.sql_compiler.process( 
    3328            generated.sqltext, include_table=False, literal_binds=True 
    3329        ) 
    3330        if generated.persisted is True: 
    3331            text += " STORED" 
    3332        elif generated.persisted is False: 
    3333            text += " VIRTUAL" 
    3334        return text 
    3335 
    3336 
    3337class GenericTypeCompiler(TypeCompiler): 
    3338    def visit_FLOAT(self, type_, **kw): 
    3339        return "FLOAT" 
    3340 
    3341    def visit_REAL(self, type_, **kw): 
    3342        return "REAL" 
    3343 
    3344    def visit_NUMERIC(self, type_, **kw): 
    3345        if type_.precision is None: 
    3346            return "NUMERIC" 
    3347        elif type_.scale is None: 
    3348            return "NUMERIC(%(precision)s)" % {"precision": type_.precision} 
    3349        else: 
    3350            return "NUMERIC(%(precision)s, %(scale)s)" % { 
    3351                "precision": type_.precision, 
    3352                "scale": type_.scale, 
    3353            } 
    3354 
    3355    def visit_DECIMAL(self, type_, **kw): 
    3356        if type_.precision is None: 
    3357            return "DECIMAL" 
    3358        elif type_.scale is None: 
    3359            return "DECIMAL(%(precision)s)" % {"precision": type_.precision} 
    3360        else: 
    3361            return "DECIMAL(%(precision)s, %(scale)s)" % { 
    3362                "precision": type_.precision, 
    3363                "scale": type_.scale, 
    3364            } 
    3365 
    3366    def visit_INTEGER(self, type_, **kw): 
    3367        return "INTEGER" 
    3368 
    3369    def visit_SMALLINT(self, type_, **kw): 
    3370        return "SMALLINT" 
    3371 
    3372    def visit_BIGINT(self, type_, **kw): 
    3373        return "BIGINT" 
    3374 
    3375    def visit_TIMESTAMP(self, type_, **kw): 
    3376        return "TIMESTAMP" 
    3377 
    3378    def visit_DATETIME(self, type_, **kw): 
    3379        return "DATETIME" 
    3380 
    3381    def visit_DATE(self, type_, **kw): 
    3382        return "DATE" 
    3383 
    3384    def visit_TIME(self, type_, **kw): 
    3385        return "TIME" 
    3386 
    3387    def visit_CLOB(self, type_, **kw): 
    3388        return "CLOB" 
    3389 
    3390    def visit_NCLOB(self, type_, **kw): 
    3391        return "NCLOB" 
    3392 
    3393    def _render_string_type(self, type_, name): 
    3394 
    3395        text = name 
    3396        if type_.length: 
    3397            text += "(%d)" % type_.length 
    3398        if type_.collation: 
    3399            text += ' COLLATE "%s"' % type_.collation 
    3400        return text 
    3401 
    3402    def visit_CHAR(self, type_, **kw): 
    3403        return self._render_string_type(type_, "CHAR") 
    3404 
    3405    def visit_NCHAR(self, type_, **kw): 
    3406        return self._render_string_type(type_, "NCHAR") 
    3407 
    3408    def visit_VARCHAR(self, type_, **kw): 
    3409        return self._render_string_type(type_, "VARCHAR") 
    3410 
    3411    def visit_NVARCHAR(self, type_, **kw): 
    3412        return self._render_string_type(type_, "NVARCHAR") 
    3413 
    3414    def visit_TEXT(self, type_, **kw): 
    3415        return self._render_string_type(type_, "TEXT") 
    3416 
    3417    def visit_BLOB(self, type_, **kw): 
    3418        return "BLOB" 
    3419 
    3420    def visit_BINARY(self, type_, **kw): 
    3421        return "BINARY" + (type_.length and "(%d)" % type_.length or "") 
    3422 
    3423    def visit_VARBINARY(self, type_, **kw): 
    3424        return "VARBINARY" + (type_.length and "(%d)" % type_.length or "") 
    3425 
    3426    def visit_BOOLEAN(self, type_, **kw): 
    3427        return "BOOLEAN" 
    3428 
    3429    def visit_large_binary(self, type_, **kw): 
    3430        return self.visit_BLOB(type_, **kw) 
    3431 
    3432    def visit_boolean(self, type_, **kw): 
    3433        return self.visit_BOOLEAN(type_, **kw) 
    3434 
    3435    def visit_time(self, type_, **kw): 
    3436        return self.visit_TIME(type_, **kw) 
    3437 
    3438    def visit_datetime(self, type_, **kw): 
    3439        return self.visit_DATETIME(type_, **kw) 
    3440 
    3441    def visit_date(self, type_, **kw): 
    3442        return self.visit_DATE(type_, **kw) 
    3443 
    3444    def visit_big_integer(self, type_, **kw): 
    3445        return self.visit_BIGINT(type_, **kw) 
    3446 
    3447    def visit_small_integer(self, type_, **kw): 
    3448        return self.visit_SMALLINT(type_, **kw) 
    3449 
    3450    def visit_integer(self, type_, **kw): 
    3451        return self.visit_INTEGER(type_, **kw) 
    3452 
    3453    def visit_real(self, type_, **kw): 
    3454        return self.visit_REAL(type_, **kw) 
    3455 
    3456    def visit_float(self, type_, **kw): 
    3457        return self.visit_FLOAT(type_, **kw) 
    3458 
    3459    def visit_numeric(self, type_, **kw): 
    3460        return self.visit_NUMERIC(type_, **kw) 
    3461 
    3462    def visit_string(self, type_, **kw): 
    3463        return self.visit_VARCHAR(type_, **kw) 
    3464 
    3465    def visit_unicode(self, type_, **kw): 
    3466        return self.visit_VARCHAR(type_, **kw) 
    3467 
    3468    def visit_text(self, type_, **kw): 
    3469        return self.visit_TEXT(type_, **kw) 
    3470 
    3471    def visit_unicode_text(self, type_, **kw): 
    3472        return self.visit_TEXT(type_, **kw) 
    3473 
    3474    def visit_enum(self, type_, **kw): 
    3475        return self.visit_VARCHAR(type_, **kw) 
    3476 
    3477    def visit_null(self, type_, **kw): 
    3478        raise exc.CompileError( 
    3479            "Can't generate DDL for %r; " 
    3480            "did you forget to specify a " 
    3481            "type on this Column?" % type_ 
    3482        ) 
    3483 
    3484    def visit_type_decorator(self, type_, **kw): 
    3485        return self.process(type_.type_engine(self.dialect), **kw) 
    3486 
    3487    def visit_user_defined(self, type_, **kw): 
    3488        return type_.get_col_spec(**kw) 
    3489 
    3490 
    3491class StrSQLTypeCompiler(GenericTypeCompiler): 
    3492    def __getattr__(self, key): 
    3493        if key.startswith("visit_"): 
    3494            return self._visit_unknown 
    3495        else: 
    3496            raise AttributeError(key) 
    3497 
    3498    def _visit_unknown(self, type_, **kw): 
    3499        return "%s" % type_.__class__.__name__ 
    3500 
    3501 
    3502class IdentifierPreparer(object): 
    3503 
    3504    """Handle quoting and case-folding of identifiers based on options.""" 
    3505 
    3506    reserved_words = RESERVED_WORDS 
    3507 
    3508    legal_characters = LEGAL_CHARACTERS 
    3509 
    3510    illegal_initial_characters = ILLEGAL_INITIAL_CHARACTERS 
    3511 
    3512    schema_for_object = schema._schema_getter(None) 
    3513 
    3514    def __init__( 
    3515        self, 
    3516        dialect, 
    3517        initial_quote='"', 
    3518        final_quote=None, 
    3519        escape_quote='"', 
    3520        quote_case_sensitive_collations=True, 
    3521        omit_schema=False, 
    3522    ): 
    3523        """Construct a new ``IdentifierPreparer`` object. 
    3524 
    3525        initial_quote 
    3526          Character that begins a delimited identifier. 
    3527 
    3528        final_quote 
    3529          Character that ends a delimited identifier. Defaults to 
    3530          `initial_quote`. 
    3531 
    3532        omit_schema 
    3533          Prevent prepending schema name. Useful for databases that do 
    3534          not support schemae. 
    3535        """ 
    3536 
    3537        self.dialect = dialect 
    3538        self.initial_quote = initial_quote 
    3539        self.final_quote = final_quote or self.initial_quote 
    3540        self.escape_quote = escape_quote 
    3541        self.escape_to_quote = self.escape_quote * 2 
    3542        self.omit_schema = omit_schema 
    3543        self.quote_case_sensitive_collations = quote_case_sensitive_collations 
    3544        self._strings = {} 
    3545        self._double_percents = self.dialect.paramstyle in ( 
    3546            "format", 
    3547            "pyformat", 
    3548        ) 
    3549 
    3550    def _with_schema_translate(self, schema_translate_map): 
    3551        prep = self.__class__.__new__(self.__class__) 
    3552        prep.__dict__.update(self.__dict__) 
    3553        prep.schema_for_object = schema._schema_getter(schema_translate_map) 
    3554        return prep 
    3555 
    3556    def _escape_identifier(self, value): 
    3557        """Escape an identifier. 
    3558 
    3559        Subclasses should override this to provide database-dependent 
    3560        escaping behavior. 
    3561        """ 
    3562 
    3563        value = value.replace(self.escape_quote, self.escape_to_quote) 
    3564        if self._double_percents: 
    3565            value = value.replace("%", "%%") 
    3566        return value 
    3567 
    3568    def _unescape_identifier(self, value): 
    3569        """Canonicalize an escaped identifier. 
    3570 
    3571        Subclasses should override this to provide database-dependent 
    3572        unescaping behavior that reverses _escape_identifier. 
    3573        """ 
    3574 
    3575        return value.replace(self.escape_to_quote, self.escape_quote) 
    3576 
    3577    def validate_sql_phrase(self, element, reg): 
    3578        """keyword sequence filter. 
    3579 
    3580        a filter for elements that are intended to represent keyword sequences, 
    3581        such as "INITIALLY", "INITIALLY DEFERRED", etc.   no special characters 
    3582        should be present. 
    3583 
    3584        .. versionadded:: 1.3 
    3585 
    3586        """ 
    3587 
    3588        if element is not None and not reg.match(element): 
    3589            raise exc.CompileError( 
    3590                "Unexpected SQL phrase: %r (matching against %r)" 
    3591                % (element, reg.pattern) 
    3592            ) 
    3593        return element 
    3594 
    3595    def quote_identifier(self, value): 
    3596        """Quote an identifier. 
    3597 
    3598        Subclasses should override this to provide database-dependent 
    3599        quoting behavior. 
    3600        """ 
    3601 
    3602        return ( 
    3603            self.initial_quote 
    3604            + self._escape_identifier(value) 
    3605            + self.final_quote 
    3606        ) 
    3607 
    3608    def _requires_quotes(self, value): 
    3609        """Return True if the given identifier requires quoting.""" 
    3610        lc_value = value.lower() 
    3611        return ( 
    3612            lc_value in self.reserved_words 
    3613            or value[0] in self.illegal_initial_characters 
    3614            or not self.legal_characters.match(util.text_type(value)) 
    3615            or (lc_value != value) 
    3616        ) 
    3617 
    3618    def _requires_quotes_illegal_chars(self, value): 
    3619        """Return True if the given identifier requires quoting, but 
    3620        not taking case convention into account.""" 
    3621        return not self.legal_characters.match(util.text_type(value)) 
    3622 
    3623    def quote_schema(self, schema, force=None): 
    3624        """Conditionally quote a schema name. 
    3625 
    3626 
    3627        The name is quoted if it is a reserved word, contains quote-necessary 
    3628        characters, or is an instance of :class:`.quoted_name` which includes 
    3629        ``quote`` set to ``True``. 
    3630 
    3631        Subclasses can override this to provide database-dependent 
    3632        quoting behavior for schema names. 
    3633 
    3634        :param schema: string schema name 
    3635        :param force: unused 
    3636 
    3637            .. deprecated:: 0.9 
    3638 
    3639                The :paramref:`.IdentifierPreparer.quote_schema.force` 
    3640                parameter is deprecated and will be removed in a future 
    3641                release.  This flag has no effect on the behavior of the 
    3642                :meth:`.IdentifierPreparer.quote` method; please refer to 
    3643                :class:`.quoted_name`. 
    3644 
    3645        """ 
    3646        if force is not None: 
    3647            # not using the util.deprecated_params() decorator in this 
    3648            # case because of the additional function call overhead on this 
    3649            # very performance-critical spot. 
    3650            util.warn_deprecated( 
    3651                "The IdentifierPreparer.quote_schema.force parameter is " 
    3652                "deprecated and will be removed in a future release.  This " 
    3653                "flag has no effect on the behavior of the " 
    3654                "IdentifierPreparer.quote method; please refer to " 
    3655                "quoted_name()." 
    3656            ) 
    3657 
    3658        return self.quote(schema) 
    3659 
    3660    def quote(self, ident, force=None): 
    3661        """Conditionally quote an identfier. 
    3662 
    3663        The identifier is quoted if it is a reserved word, contains 
    3664        quote-necessary characters, or is an instance of 
    3665        :class:`.quoted_name` which includes ``quote`` set to ``True``. 
    3666 
    3667        Subclasses can override this to provide database-dependent 
    3668        quoting behavior for identifier names. 
    3669 
    3670        :param ident: string identifier 
    3671        :param force: unused 
    3672 
    3673            .. deprecated:: 0.9 
    3674 
    3675                The :paramref:`.IdentifierPreparer.quote.force` 
    3676                parameter is deprecated and will be removed in a future 
    3677                release.  This flag has no effect on the behavior of the 
    3678                :meth:`.IdentifierPreparer.quote` method; please refer to 
    3679                :class:`.quoted_name`. 
    3680 
    3681        """ 
    3682        if force is not None: 
    3683            # not using the util.deprecated_params() decorator in this 
    3684            # case because of the additional function call overhead on this 
    3685            # very performance-critical spot. 
    3686            util.warn_deprecated( 
    3687                "The IdentifierPreparer.quote.force parameter is " 
    3688                "deprecated and will be removed in a future release.  This " 
    3689                "flag has no effect on the behavior of the " 
    3690                "IdentifierPreparer.quote method; please refer to " 
    3691                "quoted_name()." 
    3692            ) 
    3693 
    3694        force = getattr(ident, "quote", None) 
    3695 
    3696        if force is None: 
    3697            if ident in self._strings: 
    3698                return self._strings[ident] 
    3699            else: 
    3700                if self._requires_quotes(ident): 
    3701                    self._strings[ident] = self.quote_identifier(ident) 
    3702                else: 
    3703                    self._strings[ident] = ident 
    3704                return self._strings[ident] 
    3705        elif force: 
    3706            return self.quote_identifier(ident) 
    3707        else: 
    3708            return ident 
    3709 
    3710    def format_collation(self, collation_name): 
    3711        if self.quote_case_sensitive_collations: 
    3712            return self.quote(collation_name) 
    3713        else: 
    3714            return collation_name 
    3715 
    3716    def format_sequence(self, sequence, use_schema=True): 
    3717        name = self.quote(sequence.name) 
    3718 
    3719        effective_schema = self.schema_for_object(sequence) 
    3720 
    3721        if ( 
    3722            not self.omit_schema 
    3723            and use_schema 
    3724            and effective_schema is not None 
    3725        ): 
    3726            name = self.quote_schema(effective_schema) + "." + name 
    3727        return name 
    3728 
    3729    def format_label(self, label, name=None): 
    3730        return self.quote(name or label.name) 
    3731 
    3732    def format_alias(self, alias, name=None): 
    3733        return self.quote(name or alias.name) 
    3734 
    3735    def format_savepoint(self, savepoint, name=None): 
    3736        # Running the savepoint name through quoting is unnecessary 
    3737        # for all known dialects.  This is here to support potential 
    3738        # third party use cases 
    3739        ident = name or savepoint.ident 
    3740        if self._requires_quotes(ident): 
    3741            ident = self.quote_identifier(ident) 
    3742        return ident 
    3743 
    3744    @util.dependencies("sqlalchemy.sql.naming") 
    3745    def format_constraint(self, naming, constraint): 
    3746        if constraint.name is elements._NONE_NAME: 
    3747            name = naming._constraint_name_for_table( 
    3748                constraint, constraint.table 
    3749            ) 
    3750 
    3751            if name is None: 
    3752                return None 
    3753        else: 
    3754            name = constraint.name 
    3755 
    3756        if isinstance(name, elements._truncated_label): 
    3757            # calculate these at format time so that ad-hoc changes 
    3758            # to dialect.max_identifier_length etc. can be reflected 
    3759            # as IdentifierPreparer is long lived 
    3760            if constraint.__visit_name__ == "index": 
    3761                max_ = ( 
    3762                    self.dialect.max_index_name_length 
    3763                    or self.dialect.max_identifier_length 
    3764                ) 
    3765            else: 
    3766                max_ = ( 
    3767                    self.dialect.max_constraint_name_length 
    3768                    or self.dialect.max_identifier_length 
    3769                ) 
    3770            if len(name) > max_: 
    3771                name = name[0 : max_ - 8] + "_" + util.md5_hex(name)[-4:] 
    3772        else: 
    3773            self.dialect.validate_identifier(name) 
    3774 
    3775        return self.quote(name) 
    3776 
    3777    def format_index(self, index): 
    3778        return self.format_constraint(index) 
    3779 
    3780    def format_table(self, table, use_schema=True, name=None): 
    3781        """Prepare a quoted table and schema name.""" 
    3782 
    3783        if name is None: 
    3784            name = table.name 
    3785        result = self.quote(name) 
    3786 
    3787        effective_schema = self.schema_for_object(table) 
    3788 
    3789        if not self.omit_schema and use_schema and effective_schema: 
    3790            result = self.quote_schema(effective_schema) + "." + result 
    3791        return result 
    3792 
    3793    def format_schema(self, name): 
    3794        """Prepare a quoted schema name.""" 
    3795 
    3796        return self.quote(name) 
    3797 
    3798    def format_column( 
    3799        self, 
    3800        column, 
    3801        use_table=False, 
    3802        name=None, 
    3803        table_name=None, 
    3804        use_schema=False, 
    3805    ): 
    3806        """Prepare a quoted column name.""" 
    3807 
    3808        if name is None: 
    3809            name = column.name 
    3810        if not getattr(column, "is_literal", False): 
    3811            if use_table: 
    3812                return ( 
    3813                    self.format_table( 
    3814                        column.table, use_schema=use_schema, name=table_name 
    3815                    ) 
    3816                    + "." 
    3817                    + self.quote(name) 
    3818                ) 
    3819            else: 
    3820                return self.quote(name) 
    3821        else: 
    3822            # literal textual elements get stuck into ColumnClause a lot, 
    3823            # which shouldn't get quoted 
    3824 
    3825            if use_table: 
    3826                return ( 
    3827                    self.format_table( 
    3828                        column.table, use_schema=use_schema, name=table_name 
    3829                    ) 
    3830                    + "." 
    3831                    + name 
    3832                ) 
    3833            else: 
    3834                return name 
    3835 
    3836    def format_table_seq(self, table, use_schema=True): 
    3837        """Format table name and schema as a tuple.""" 
    3838 
    3839        # Dialects with more levels in their fully qualified references 
    3840        # ('database', 'owner', etc.) could override this and return 
    3841        # a longer sequence. 
    3842 
    3843        effective_schema = self.schema_for_object(table) 
    3844 
    3845        if not self.omit_schema and use_schema and effective_schema: 
    3846            return ( 
    3847                self.quote_schema(effective_schema), 
    3848                self.format_table(table, use_schema=False), 
    3849            ) 
    3850        else: 
    3851            return (self.format_table(table, use_schema=False),) 
    3852 
    3853    @util.memoized_property 
    3854    def _r_identifiers(self): 
    3855        initial, final, escaped_final = [ 
    3856            re.escape(s) 
    3857            for s in ( 
    3858                self.initial_quote, 
    3859                self.final_quote, 
    3860                self._escape_identifier(self.final_quote), 
    3861            ) 
    3862        ] 
    3863        r = re.compile( 
    3864            r"(?:" 
    3865            r"(?:%(initial)s((?:%(escaped)s|[^%(final)s])+)%(final)s" 
    3866            r"|([^\.]+))(?=\.|$))+" 
    3867            % {"initial": initial, "final": final, "escaped": escaped_final} 
    3868        ) 
    3869        return r 
    3870 
    3871    def unformat_identifiers(self, identifiers): 
    3872        """Unpack 'schema.table.column'-like strings into components.""" 
    3873 
    3874        r = self._r_identifiers 
    3875        return [ 
    3876            self._unescape_identifier(i) 
    3877            for i in [a or b for a, b in r.findall(identifiers)] 
    3878        ]