1# sql/base.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: https://www.opensource.org/licenses/mit-license.php 7 8"""Foundational utilities common to many sql modules. 9 10""" 11 12 13import itertools 14import operator 15import re 16 17from . import roles 18from . import visitors 19from .traversals import HasCacheKey # noqa 20from .traversals import HasCopyInternals # noqa 21from .traversals import MemoizedHasCacheKey # noqa 22from .visitors import ClauseVisitor 23from .visitors import ExtendedInternalTraversal 24from .visitors import InternalTraversal 25from .. import exc 26from .. import util 27from ..util import HasMemoized 28from ..util import hybridmethod 29 30 31coercions = None 32elements = None 33type_api = None 34 35PARSE_AUTOCOMMIT = util.symbol("PARSE_AUTOCOMMIT") 36NO_ARG = util.symbol("NO_ARG") 37 38 39class Immutable(object): 40 """mark a ClauseElement as 'immutable' when expressions are cloned.""" 41 42 _is_immutable = True 43 44 def unique_params(self, *optionaldict, **kwargs): 45 raise NotImplementedError("Immutable objects do not support copying") 46 47 def params(self, *optionaldict, **kwargs): 48 raise NotImplementedError("Immutable objects do not support copying") 49 50 def _clone(self, **kw): 51 return self 52 53 def _copy_internals(self, **kw): 54 pass 55 56 57class SingletonConstant(Immutable): 58 """Represent SQL constants like NULL, TRUE, FALSE""" 59 60 _is_singleton_constant = True 61 62 def __new__(cls, *arg, **kw): 63 return cls._singleton 64 65 @classmethod 66 def _create_singleton(cls): 67 obj = object.__new__(cls) 68 obj.__init__() 69 70 # for a long time this was an empty frozenset, meaning 71 # a SingletonConstant would never be a "corresponding column" in 72 # a statement. This referred to #6259. However, in #7154 we see 73 # that we do in fact need "correspondence" to work when matching cols 74 # in result sets, so the non-correspondence was moved to a more 75 # specific level when we are actually adapting expressions for SQL 76 # render only. 77 obj.proxy_set = frozenset([obj]) 78 cls._singleton = obj 79 80 81def _from_objects(*elements): 82 return itertools.chain.from_iterable( 83 [element._from_objects for element in elements] 84 ) 85 86 87def _select_iterables(elements): 88 """expand tables into individual columns in the 89 given list of column expressions. 90 91 """ 92 return itertools.chain.from_iterable( 93 [c._select_iterable for c in elements] 94 ) 95 96 97def _generative(fn): 98 """non-caching _generative() decorator. 99 100 This is basically the legacy decorator that copies the object and 101 runs a method on the new copy. 102 103 """ 104 105 @util.decorator 106 def _generative(fn, self, *args, **kw): 107 """Mark a method as generative.""" 108 109 self = self._generate() 110 x = fn(self, *args, **kw) 111 assert x is None, "generative methods must have no return value" 112 return self 113 114 decorated = _generative(fn) 115 decorated.non_generative = fn 116 return decorated 117 118 119def _exclusive_against(*names, **kw): 120 msgs = kw.pop("msgs", {}) 121 122 defaults = kw.pop("defaults", {}) 123 124 getters = [ 125 (name, operator.attrgetter(name), defaults.get(name, None)) 126 for name in names 127 ] 128 129 @util.decorator 130 def check(fn, *args, **kw): 131 # make pylance happy by not including "self" in the argument 132 # list 133 self = args[0] 134 args = args[1:] 135 for name, getter, default_ in getters: 136 if getter(self) is not default_: 137 msg = msgs.get( 138 name, 139 "Method %s() has already been invoked on this %s construct" 140 % (fn.__name__, self.__class__), 141 ) 142 raise exc.InvalidRequestError(msg) 143 return fn(self, *args, **kw) 144 145 return check 146 147 148def _clone(element, **kw): 149 return element._clone(**kw) 150 151 152def _expand_cloned(elements): 153 """expand the given set of ClauseElements to be the set of all 'cloned' 154 predecessors. 155 156 """ 157 return itertools.chain(*[x._cloned_set for x in elements]) 158 159 160def _cloned_intersection(a, b): 161 """return the intersection of sets a and b, counting 162 any overlap between 'cloned' predecessors. 163 164 The returned set is in terms of the entities present within 'a'. 165 166 """ 167 all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b)) 168 return set( 169 elem for elem in a if all_overlap.intersection(elem._cloned_set) 170 ) 171 172 173def _cloned_difference(a, b): 174 all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b)) 175 return set( 176 elem for elem in a if not all_overlap.intersection(elem._cloned_set) 177 ) 178 179 180class _DialectArgView(util.collections_abc.MutableMapping): 181 """A dictionary view of dialect-level arguments in the form 182 <dialectname>_<argument_name>. 183 184 """ 185 186 def __init__(self, obj): 187 self.obj = obj 188 189 def _key(self, key): 190 try: 191 dialect, value_key = key.split("_", 1) 192 except ValueError as err: 193 util.raise_(KeyError(key), replace_context=err) 194 else: 195 return dialect, value_key 196 197 def __getitem__(self, key): 198 dialect, value_key = self._key(key) 199 200 try: 201 opt = self.obj.dialect_options[dialect] 202 except exc.NoSuchModuleError as err: 203 util.raise_(KeyError(key), replace_context=err) 204 else: 205 return opt[value_key] 206 207 def __setitem__(self, key, value): 208 try: 209 dialect, value_key = self._key(key) 210 except KeyError as err: 211 util.raise_( 212 exc.ArgumentError( 213 "Keys must be of the form <dialectname>_<argname>" 214 ), 215 replace_context=err, 216 ) 217 else: 218 self.obj.dialect_options[dialect][value_key] = value 219 220 def __delitem__(self, key): 221 dialect, value_key = self._key(key) 222 del self.obj.dialect_options[dialect][value_key] 223 224 def __len__(self): 225 return sum( 226 len(args._non_defaults) 227 for args in self.obj.dialect_options.values() 228 ) 229 230 def __iter__(self): 231 return ( 232 "%s_%s" % (dialect_name, value_name) 233 for dialect_name in self.obj.dialect_options 234 for value_name in self.obj.dialect_options[ 235 dialect_name 236 ]._non_defaults 237 ) 238 239 240class _DialectArgDict(util.collections_abc.MutableMapping): 241 """A dictionary view of dialect-level arguments for a specific 242 dialect. 243 244 Maintains a separate collection of user-specified arguments 245 and dialect-specified default arguments. 246 247 """ 248 249 def __init__(self): 250 self._non_defaults = {} 251 self._defaults = {} 252 253 def __len__(self): 254 return len(set(self._non_defaults).union(self._defaults)) 255 256 def __iter__(self): 257 return iter(set(self._non_defaults).union(self._defaults)) 258 259 def __getitem__(self, key): 260 if key in self._non_defaults: 261 return self._non_defaults[key] 262 else: 263 return self._defaults[key] 264 265 def __setitem__(self, key, value): 266 self._non_defaults[key] = value 267 268 def __delitem__(self, key): 269 del self._non_defaults[key] 270 271 272@util.preload_module("sqlalchemy.dialects") 273def _kw_reg_for_dialect(dialect_name): 274 dialect_cls = util.preloaded.dialects.registry.load(dialect_name) 275 if dialect_cls.construct_arguments is None: 276 return None 277 return dict(dialect_cls.construct_arguments) 278 279 280class DialectKWArgs(object): 281 """Establish the ability for a class to have dialect-specific arguments 282 with defaults and constructor validation. 283 284 The :class:`.DialectKWArgs` interacts with the 285 :attr:`.DefaultDialect.construct_arguments` present on a dialect. 286 287 .. seealso:: 288 289 :attr:`.DefaultDialect.construct_arguments` 290 291 """ 292 293 _dialect_kwargs_traverse_internals = [ 294 ("dialect_options", InternalTraversal.dp_dialect_options) 295 ] 296 297 @classmethod 298 def argument_for(cls, dialect_name, argument_name, default): 299 """Add a new kind of dialect-specific keyword argument for this class. 300 301 E.g.:: 302 303 Index.argument_for("mydialect", "length", None) 304 305 some_index = Index('a', 'b', mydialect_length=5) 306 307 The :meth:`.DialectKWArgs.argument_for` method is a per-argument 308 way adding extra arguments to the 309 :attr:`.DefaultDialect.construct_arguments` dictionary. This 310 dictionary provides a list of argument names accepted by various 311 schema-level constructs on behalf of a dialect. 312 313 New dialects should typically specify this dictionary all at once as a 314 data member of the dialect class. The use case for ad-hoc addition of 315 argument names is typically for end-user code that is also using 316 a custom compilation scheme which consumes the additional arguments. 317 318 :param dialect_name: name of a dialect. The dialect must be 319 locatable, else a :class:`.NoSuchModuleError` is raised. The 320 dialect must also include an existing 321 :attr:`.DefaultDialect.construct_arguments` collection, indicating 322 that it participates in the keyword-argument validation and default 323 system, else :class:`.ArgumentError` is raised. If the dialect does 324 not include this collection, then any keyword argument can be 325 specified on behalf of this dialect already. All dialects packaged 326 within SQLAlchemy include this collection, however for third party 327 dialects, support may vary. 328 329 :param argument_name: name of the parameter. 330 331 :param default: default value of the parameter. 332 333 .. versionadded:: 0.9.4 334 335 """ 336 337 construct_arg_dictionary = DialectKWArgs._kw_registry[dialect_name] 338 if construct_arg_dictionary is None: 339 raise exc.ArgumentError( 340 "Dialect '%s' does have keyword-argument " 341 "validation and defaults enabled configured" % dialect_name 342 ) 343 if cls not in construct_arg_dictionary: 344 construct_arg_dictionary[cls] = {} 345 construct_arg_dictionary[cls][argument_name] = default 346 347 @util.memoized_property 348 def dialect_kwargs(self): 349 """A collection of keyword arguments specified as dialect-specific 350 options to this construct. 351 352 The arguments are present here in their original ``<dialect>_<kwarg>`` 353 format. Only arguments that were actually passed are included; 354 unlike the :attr:`.DialectKWArgs.dialect_options` collection, which 355 contains all options known by this dialect including defaults. 356 357 The collection is also writable; keys are accepted of the 358 form ``<dialect>_<kwarg>`` where the value will be assembled 359 into the list of options. 360 361 .. versionadded:: 0.9.2 362 363 .. versionchanged:: 0.9.4 The :attr:`.DialectKWArgs.dialect_kwargs` 364 collection is now writable. 365 366 .. seealso:: 367 368 :attr:`.DialectKWArgs.dialect_options` - nested dictionary form 369 370 """ 371 return _DialectArgView(self) 372 373 @property 374 def kwargs(self): 375 """A synonym for :attr:`.DialectKWArgs.dialect_kwargs`.""" 376 return self.dialect_kwargs 377 378 _kw_registry = util.PopulateDict(_kw_reg_for_dialect) 379 380 def _kw_reg_for_dialect_cls(self, dialect_name): 381 construct_arg_dictionary = DialectKWArgs._kw_registry[dialect_name] 382 d = _DialectArgDict() 383 384 if construct_arg_dictionary is None: 385 d._defaults.update({"*": None}) 386 else: 387 for cls in reversed(self.__class__.__mro__): 388 if cls in construct_arg_dictionary: 389 d._defaults.update(construct_arg_dictionary[cls]) 390 return d 391 392 @util.memoized_property 393 def dialect_options(self): 394 """A collection of keyword arguments specified as dialect-specific 395 options to this construct. 396 397 This is a two-level nested registry, keyed to ``<dialect_name>`` 398 and ``<argument_name>``. For example, the ``postgresql_where`` 399 argument would be locatable as:: 400 401 arg = my_object.dialect_options['postgresql']['where'] 402 403 .. versionadded:: 0.9.2 404 405 .. seealso:: 406 407 :attr:`.DialectKWArgs.dialect_kwargs` - flat dictionary form 408 409 """ 410 411 return util.PopulateDict( 412 util.portable_instancemethod(self._kw_reg_for_dialect_cls) 413 ) 414 415 def _validate_dialect_kwargs(self, kwargs): 416 # validate remaining kwargs that they all specify DB prefixes 417 418 if not kwargs: 419 return 420 421 for k in kwargs: 422 m = re.match("^(.+?)_(.+)$", k) 423 if not m: 424 raise TypeError( 425 "Additional arguments should be " 426 "named <dialectname>_<argument>, got '%s'" % k 427 ) 428 dialect_name, arg_name = m.group(1, 2) 429 430 try: 431 construct_arg_dictionary = self.dialect_options[dialect_name] 432 except exc.NoSuchModuleError: 433 util.warn( 434 "Can't validate argument %r; can't " 435 "locate any SQLAlchemy dialect named %r" 436 % (k, dialect_name) 437 ) 438 self.dialect_options[dialect_name] = d = _DialectArgDict() 439 d._defaults.update({"*": None}) 440 d._non_defaults[arg_name] = kwargs[k] 441 else: 442 if ( 443 "*" not in construct_arg_dictionary 444 and arg_name not in construct_arg_dictionary 445 ): 446 raise exc.ArgumentError( 447 "Argument %r is not accepted by " 448 "dialect %r on behalf of %r" 449 % (k, dialect_name, self.__class__) 450 ) 451 else: 452 construct_arg_dictionary[arg_name] = kwargs[k] 453 454 455class CompileState(object): 456 """Produces additional object state necessary for a statement to be 457 compiled. 458 459 the :class:`.CompileState` class is at the base of classes that assemble 460 state for a particular statement object that is then used by the 461 compiler. This process is essentially an extension of the process that 462 the SQLCompiler.visit_XYZ() method takes, however there is an emphasis 463 on converting raw user intent into more organized structures rather than 464 producing string output. The top-level :class:`.CompileState` for the 465 statement being executed is also accessible when the execution context 466 works with invoking the statement and collecting results. 467 468 The production of :class:`.CompileState` is specific to the compiler, such 469 as within the :meth:`.SQLCompiler.visit_insert`, 470 :meth:`.SQLCompiler.visit_select` etc. methods. These methods are also 471 responsible for associating the :class:`.CompileState` with the 472 :class:`.SQLCompiler` itself, if the statement is the "toplevel" statement, 473 i.e. the outermost SQL statement that's actually being executed. 474 There can be other :class:`.CompileState` objects that are not the 475 toplevel, such as when a SELECT subquery or CTE-nested 476 INSERT/UPDATE/DELETE is generated. 477 478 .. versionadded:: 1.4 479 480 """ 481 482 __slots__ = ("statement",) 483 484 plugins = {} 485 486 @classmethod 487 def create_for_statement(cls, statement, compiler, **kw): 488 # factory construction. 489 490 if statement._propagate_attrs: 491 plugin_name = statement._propagate_attrs.get( 492 "compile_state_plugin", "default" 493 ) 494 klass = cls.plugins.get( 495 (plugin_name, statement._effective_plugin_target), None 496 ) 497 if klass is None: 498 klass = cls.plugins[ 499 ("default", statement._effective_plugin_target) 500 ] 501 502 else: 503 klass = cls.plugins[ 504 ("default", statement._effective_plugin_target) 505 ] 506 507 if klass is cls: 508 return cls(statement, compiler, **kw) 509 else: 510 return klass.create_for_statement(statement, compiler, **kw) 511 512 def __init__(self, statement, compiler, **kw): 513 self.statement = statement 514 515 @classmethod 516 def get_plugin_class(cls, statement): 517 plugin_name = statement._propagate_attrs.get( 518 "compile_state_plugin", None 519 ) 520 521 if plugin_name: 522 key = (plugin_name, statement._effective_plugin_target) 523 if key in cls.plugins: 524 return cls.plugins[key] 525 526 # there's no case where we call upon get_plugin_class() and want 527 # to get None back, there should always be a default. return that 528 # if there was no plugin-specific class (e.g. Insert with "orm" 529 # plugin) 530 try: 531 return cls.plugins[("default", statement._effective_plugin_target)] 532 except KeyError: 533 return None 534 535 @classmethod 536 def _get_plugin_class_for_plugin(cls, statement, plugin_name): 537 try: 538 return cls.plugins[ 539 (plugin_name, statement._effective_plugin_target) 540 ] 541 except KeyError: 542 return None 543 544 @classmethod 545 def plugin_for(cls, plugin_name, visit_name): 546 def decorate(cls_to_decorate): 547 cls.plugins[(plugin_name, visit_name)] = cls_to_decorate 548 return cls_to_decorate 549 550 return decorate 551 552 553class Generative(HasMemoized): 554 """Provide a method-chaining pattern in conjunction with the 555 @_generative decorator.""" 556 557 def _generate(self): 558 skip = self._memoized_keys 559 cls = self.__class__ 560 s = cls.__new__(cls) 561 if skip: 562 s.__dict__ = { 563 k: v for k, v in self.__dict__.items() if k not in skip 564 } 565 else: 566 s.__dict__ = self.__dict__.copy() 567 return s 568 569 570class InPlaceGenerative(HasMemoized): 571 """Provide a method-chaining pattern in conjunction with the 572 @_generative decorator that mutates in place.""" 573 574 def _generate(self): 575 skip = self._memoized_keys 576 for k in skip: 577 self.__dict__.pop(k, None) 578 return self 579 580 581class HasCompileState(Generative): 582 """A class that has a :class:`.CompileState` associated with it.""" 583 584 _compile_state_plugin = None 585 586 _attributes = util.immutabledict() 587 588 _compile_state_factory = CompileState.create_for_statement 589 590 591class _MetaOptions(type): 592 """metaclass for the Options class.""" 593 594 def __init__(cls, classname, bases, dict_): 595 cls._cache_attrs = tuple( 596 sorted( 597 d 598 for d in dict_ 599 if not d.startswith("__") 600 and d not in ("_cache_key_traversal",) 601 ) 602 ) 603 type.__init__(cls, classname, bases, dict_) 604 605 def __add__(self, other): 606 o1 = self() 607 608 if set(other).difference(self._cache_attrs): 609 raise TypeError( 610 "dictionary contains attributes not covered by " 611 "Options class %s: %r" 612 % (self, set(other).difference(self._cache_attrs)) 613 ) 614 615 o1.__dict__.update(other) 616 return o1 617 618 619class Options(util.with_metaclass(_MetaOptions)): 620 """A cacheable option dictionary with defaults.""" 621 622 def __init__(self, **kw): 623 self.__dict__.update(kw) 624 625 def __add__(self, other): 626 o1 = self.__class__.__new__(self.__class__) 627 o1.__dict__.update(self.__dict__) 628 629 if set(other).difference(self._cache_attrs): 630 raise TypeError( 631 "dictionary contains attributes not covered by " 632 "Options class %s: %r" 633 % (self, set(other).difference(self._cache_attrs)) 634 ) 635 636 o1.__dict__.update(other) 637 return o1 638 639 def __eq__(self, other): 640 # TODO: very inefficient. This is used only in test suites 641 # right now. 642 for a, b in util.zip_longest(self._cache_attrs, other._cache_attrs): 643 if getattr(self, a) != getattr(other, b): 644 return False 645 return True 646 647 def __repr__(self): 648 # TODO: fairly inefficient, used only in debugging right now. 649 650 return "%s(%s)" % ( 651 self.__class__.__name__, 652 ", ".join( 653 "%s=%r" % (k, self.__dict__[k]) 654 for k in self._cache_attrs 655 if k in self.__dict__ 656 ), 657 ) 658 659 @classmethod 660 def isinstance(cls, klass): 661 return issubclass(cls, klass) 662 663 @hybridmethod 664 def add_to_element(self, name, value): 665 return self + {name: getattr(self, name) + value} 666 667 @hybridmethod 668 def _state_dict(self): 669 return self.__dict__ 670 671 _state_dict_const = util.immutabledict() 672 673 @_state_dict.classlevel 674 def _state_dict(cls): 675 return cls._state_dict_const 676 677 @classmethod 678 def safe_merge(cls, other): 679 d = other._state_dict() 680 681 # only support a merge with another object of our class 682 # and which does not have attrs that we don't. otherwise 683 # we risk having state that might not be part of our cache 684 # key strategy 685 686 if ( 687 cls is not other.__class__ 688 and other._cache_attrs 689 and set(other._cache_attrs).difference(cls._cache_attrs) 690 ): 691 raise TypeError( 692 "other element %r is not empty, is not of type %s, " 693 "and contains attributes not covered here %r" 694 % ( 695 other, 696 cls, 697 set(other._cache_attrs).difference(cls._cache_attrs), 698 ) 699 ) 700 return cls + d 701 702 @classmethod 703 def from_execution_options( 704 cls, key, attrs, exec_options, statement_exec_options 705 ): 706 """process Options argument in terms of execution options. 707 708 709 e.g.:: 710 711 ( 712 load_options, 713 execution_options, 714 ) = QueryContext.default_load_options.from_execution_options( 715 "_sa_orm_load_options", 716 { 717 "populate_existing", 718 "autoflush", 719 "yield_per" 720 }, 721 execution_options, 722 statement._execution_options, 723 ) 724 725 get back the Options and refresh "_sa_orm_load_options" in the 726 exec options dict w/ the Options as well 727 728 """ 729 730 # common case is that no options we are looking for are 731 # in either dictionary, so cancel for that first 732 check_argnames = attrs.intersection( 733 set(exec_options).union(statement_exec_options) 734 ) 735 736 existing_options = exec_options.get(key, cls) 737 738 if check_argnames: 739 result = {} 740 for argname in check_argnames: 741 local = "_" + argname 742 if argname in exec_options: 743 result[local] = exec_options[argname] 744 elif argname in statement_exec_options: 745 result[local] = statement_exec_options[argname] 746 747 new_options = existing_options + result 748 exec_options = util.immutabledict().merge_with( 749 exec_options, {key: new_options} 750 ) 751 return new_options, exec_options 752 753 else: 754 return existing_options, exec_options 755 756 757class CacheableOptions(Options, HasCacheKey): 758 @hybridmethod 759 def _gen_cache_key(self, anon_map, bindparams): 760 return HasCacheKey._gen_cache_key(self, anon_map, bindparams) 761 762 @_gen_cache_key.classlevel 763 def _gen_cache_key(cls, anon_map, bindparams): 764 return (cls, ()) 765 766 @hybridmethod 767 def _generate_cache_key(self): 768 return HasCacheKey._generate_cache_key_for_object(self) 769 770 771class ExecutableOption(HasCopyInternals, HasCacheKey): 772 _annotations = util.EMPTY_DICT 773 774 __visit_name__ = "executable_option" 775 776 def _clone(self, **kw): 777 """Create a shallow copy of this ExecutableOption.""" 778 c = self.__class__.__new__(self.__class__) 779 c.__dict__ = dict(self.__dict__) 780 return c 781 782 783class Executable(roles.StatementRole, Generative): 784 """Mark a :class:`_expression.ClauseElement` as supporting execution. 785 786 :class:`.Executable` is a superclass for all "statement" types 787 of objects, including :func:`select`, :func:`delete`, :func:`update`, 788 :func:`insert`, :func:`text`. 789 790 """ 791 792 supports_execution = True 793 _execution_options = util.immutabledict() 794 _bind = None 795 _with_options = () 796 _with_context_options = () 797 798 _executable_traverse_internals = [ 799 ("_with_options", InternalTraversal.dp_executable_options), 800 ( 801 "_with_context_options", 802 ExtendedInternalTraversal.dp_with_context_options, 803 ), 804 ("_propagate_attrs", ExtendedInternalTraversal.dp_propagate_attrs), 805 ] 806 807 is_select = False 808 is_update = False 809 is_insert = False 810 is_text = False 811 is_delete = False 812 is_dml = False 813 814 @property 815 def _effective_plugin_target(self): 816 return self.__visit_name__ 817 818 @_generative 819 def options(self, *options): 820 """Apply options to this statement. 821 822 In the general sense, options are any kind of Python object 823 that can be interpreted by the SQL compiler for the statement. 824 These options can be consumed by specific dialects or specific kinds 825 of compilers. 826 827 The most commonly known kind of option are the ORM level options 828 that apply "eager load" and other loading behaviors to an ORM 829 query. However, options can theoretically be used for many other 830 purposes. 831 832 For background on specific kinds of options for specific kinds of 833 statements, refer to the documentation for those option objects. 834 835 .. versionchanged:: 1.4 - added :meth:`.Generative.options` to 836 Core statement objects towards the goal of allowing unified 837 Core / ORM querying capabilities. 838 839 .. seealso:: 840 841 :ref:`deferred_options` - refers to options specific to the usage 842 of ORM queries 843 844 :ref:`relationship_loader_options` - refers to options specific 845 to the usage of ORM queries 846 847 """ 848 self._with_options += tuple( 849 coercions.expect(roles.HasCacheKeyRole, opt) for opt in options 850 ) 851 852 @_generative 853 def _set_compile_options(self, compile_options): 854 """Assign the compile options to a new value. 855 856 :param compile_options: appropriate CacheableOptions structure 857 858 """ 859 860 self._compile_options = compile_options 861 862 @_generative 863 def _update_compile_options(self, options): 864 """update the _compile_options with new keys.""" 865 866 self._compile_options += options 867 868 @_generative 869 def _add_context_option(self, callable_, cache_args): 870 """Add a context option to this statement. 871 872 These are callable functions that will 873 be given the CompileState object upon compilation. 874 875 A second argument cache_args is required, which will be combined with 876 the ``__code__`` identity of the function itself in order to produce a 877 cache key. 878 879 """ 880 self._with_context_options += ((callable_, cache_args),) 881 882 @_generative 883 def execution_options(self, **kw): 884 """Set non-SQL options for the statement which take effect during 885 execution. 886 887 Execution options can be set on a per-statement or 888 per :class:`_engine.Connection` basis. Additionally, the 889 :class:`_engine.Engine` and ORM :class:`~.orm.query.Query` 890 objects provide 891 access to execution options which they in turn configure upon 892 connections. 893 894 The :meth:`execution_options` method is generative. A new 895 instance of this statement is returned that contains the options:: 896 897 statement = select(table.c.x, table.c.y) 898 statement = statement.execution_options(autocommit=True) 899 900 Note that only a subset of possible execution options can be applied 901 to a statement - these include "autocommit" and "stream_results", 902 but not "isolation_level" or "compiled_cache". 903 See :meth:`_engine.Connection.execution_options` for a full list of 904 possible options. 905 906 .. seealso:: 907 908 :meth:`_engine.Connection.execution_options` 909 910 :meth:`_query.Query.execution_options` 911 912 :meth:`.Executable.get_execution_options` 913 914 """ 915 if "isolation_level" in kw: 916 raise exc.ArgumentError( 917 "'isolation_level' execution option may only be specified " 918 "on Connection.execution_options(), or " 919 "per-engine using the isolation_level " 920 "argument to create_engine()." 921 ) 922 if "compiled_cache" in kw: 923 raise exc.ArgumentError( 924 "'compiled_cache' execution option may only be specified " 925 "on Connection.execution_options(), not per statement." 926 ) 927 self._execution_options = self._execution_options.union(kw) 928 929 def get_execution_options(self): 930 """Get the non-SQL options which will take effect during execution. 931 932 .. versionadded:: 1.3 933 934 .. seealso:: 935 936 :meth:`.Executable.execution_options` 937 """ 938 return self._execution_options 939 940 @util.deprecated_20( 941 ":meth:`.Executable.execute`", 942 alternative="All statement execution in SQLAlchemy 2.0 is performed " 943 "by the :meth:`_engine.Connection.execute` method of " 944 ":class:`_engine.Connection`, " 945 "or in the ORM by the :meth:`.Session.execute` method of " 946 ":class:`.Session`.", 947 ) 948 def execute(self, *multiparams, **params): 949 """Compile and execute this :class:`.Executable`.""" 950 e = self.bind 951 if e is None: 952 label = ( 953 getattr(self, "description", None) or self.__class__.__name__ 954 ) 955 msg = ( 956 "This %s is not directly bound to a Connection or Engine. " 957 "Use the .execute() method of a Connection or Engine " 958 "to execute this construct." % label 959 ) 960 raise exc.UnboundExecutionError(msg) 961 return e._execute_clauseelement( 962 self, multiparams, params, util.immutabledict() 963 ) 964 965 @util.deprecated_20( 966 ":meth:`.Executable.scalar`", 967 alternative="Scalar execution in SQLAlchemy 2.0 is performed " 968 "by the :meth:`_engine.Connection.scalar` method of " 969 ":class:`_engine.Connection`, " 970 "or in the ORM by the :meth:`.Session.scalar` method of " 971 ":class:`.Session`.", 972 ) 973 def scalar(self, *multiparams, **params): 974 """Compile and execute this :class:`.Executable`, returning the 975 result's scalar representation. 976 977 """ 978 return self.execute(*multiparams, **params).scalar() 979 980 @property 981 @util.deprecated_20( 982 ":attr:`.Executable.bind`", 983 alternative="Bound metadata is being removed as of SQLAlchemy 2.0.", 984 enable_warnings=False, 985 ) 986 def bind(self): 987 """Returns the :class:`_engine.Engine` or :class:`_engine.Connection` 988 to 989 which this :class:`.Executable` is bound, or None if none found. 990 991 This is a traversal which checks locally, then 992 checks among the "from" clauses of associated objects 993 until a bound engine or connection is found. 994 995 """ 996 if self._bind is not None: 997 return self._bind 998 999 for f in _from_objects(self): 1000 if f is self: 1001 continue 1002 engine = f.bind 1003 if engine is not None: 1004 return engine 1005 else: 1006 return None 1007 1008 1009class prefix_anon_map(dict): 1010 """A map that creates new keys for missing key access. 1011 1012 Considers keys of the form "<ident> <name>" to produce 1013 new symbols "<name>_<index>", where "index" is an incrementing integer 1014 corresponding to <name>. 1015 1016 Inlines the approach taken by :class:`sqlalchemy.util.PopulateDict` which 1017 is otherwise usually used for this type of operation. 1018 1019 """ 1020 1021 def __missing__(self, key): 1022 (ident, derived) = key.split(" ", 1) 1023 anonymous_counter = self.get(derived, 1) 1024 self[derived] = anonymous_counter + 1 1025 value = derived + "_" + str(anonymous_counter) 1026 self[key] = value 1027 return value 1028 1029 1030class SchemaEventTarget(object): 1031 """Base class for elements that are the targets of :class:`.DDLEvents` 1032 events. 1033 1034 This includes :class:`.SchemaItem` as well as :class:`.SchemaType`. 1035 1036 """ 1037 1038 def _set_parent(self, parent, **kw): 1039 """Associate with this SchemaEvent's parent object.""" 1040 1041 def _set_parent_with_dispatch(self, parent, **kw): 1042 self.dispatch.before_parent_attach(self, parent) 1043 self._set_parent(parent, **kw) 1044 self.dispatch.after_parent_attach(self, parent) 1045 1046 1047class SchemaVisitor(ClauseVisitor): 1048 """Define the visiting for ``SchemaItem`` objects.""" 1049 1050 __traverse_options__ = {"schema_visitor": True} 1051 1052 1053class ColumnCollection(object): 1054 """Collection of :class:`_expression.ColumnElement` instances, 1055 typically for 1056 :class:`_sql.FromClause` objects. 1057 1058 The :class:`_sql.ColumnCollection` object is most commonly available 1059 as the :attr:`_schema.Table.c` or :attr:`_schema.Table.columns` collection 1060 on the :class:`_schema.Table` object, introduced at 1061 :ref:`metadata_tables_and_columns`. 1062 1063 The :class:`_expression.ColumnCollection` has both mapping- and sequence- 1064 like behaviors. A :class:`_expression.ColumnCollection` usually stores 1065 :class:`_schema.Column` objects, which are then accessible both via mapping 1066 style access as well as attribute access style. 1067 1068 To access :class:`_schema.Column` objects using ordinary attribute-style 1069 access, specify the name like any other object attribute, such as below 1070 a column named ``employee_name`` is accessed:: 1071 1072 >>> employee_table.c.employee_name 1073 1074 To access columns that have names with special characters or spaces, 1075 index-style access is used, such as below which illustrates a column named 1076 ``employee ' payment`` is accessed:: 1077 1078 >>> employee_table.c["employee ' payment"] 1079 1080 As the :class:`_sql.ColumnCollection` object provides a Python dictionary 1081 interface, common dictionary method names like 1082 :meth:`_sql.ColumnCollection.keys`, :meth:`_sql.ColumnCollection.values`, 1083 and :meth:`_sql.ColumnCollection.items` are available, which means that 1084 database columns that are keyed under these names also need to use indexed 1085 access:: 1086 1087 >>> employee_table.c["values"] 1088 1089 1090 The name for which a :class:`_schema.Column` would be present is normally 1091 that of the :paramref:`_schema.Column.key` parameter. In some contexts, 1092 such as a :class:`_sql.Select` object that uses a label style set 1093 using the :meth:`_sql.Select.set_label_style` method, a column of a certain 1094 key may instead be represented under a particular label name such 1095 as ``tablename_columnname``:: 1096 1097 >>> from sqlalchemy import select, column, table 1098 >>> from sqlalchemy import LABEL_STYLE_TABLENAME_PLUS_COL 1099 >>> t = table("t", column("c")) 1100 >>> stmt = select(t).set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) 1101 >>> subq = stmt.subquery() 1102 >>> subq.c.t_c 1103 <sqlalchemy.sql.elements.ColumnClause at 0x7f59dcf04fa0; t_c> 1104 1105 :class:`.ColumnCollection` also indexes the columns in order and allows 1106 them to be accessible by their integer position:: 1107 1108 >>> cc[0] 1109 Column('x', Integer(), table=None) 1110 >>> cc[1] 1111 Column('y', Integer(), table=None) 1112 1113 .. versionadded:: 1.4 :class:`_expression.ColumnCollection` 1114 allows integer-based 1115 index access to the collection. 1116 1117 Iterating the collection yields the column expressions in order:: 1118 1119 >>> list(cc) 1120 [Column('x', Integer(), table=None), 1121 Column('y', Integer(), table=None)] 1122 1123 The base :class:`_expression.ColumnCollection` object can store 1124 duplicates, which can 1125 mean either two columns with the same key, in which case the column 1126 returned by key access is **arbitrary**:: 1127 1128 >>> x1, x2 = Column('x', Integer), Column('x', Integer) 1129 >>> cc = ColumnCollection(columns=[(x1.name, x1), (x2.name, x2)]) 1130 >>> list(cc) 1131 [Column('x', Integer(), table=None), 1132 Column('x', Integer(), table=None)] 1133 >>> cc['x'] is x1 1134 False 1135 >>> cc['x'] is x2 1136 True 1137 1138 Or it can also mean the same column multiple times. These cases are 1139 supported as :class:`_expression.ColumnCollection` 1140 is used to represent the columns in 1141 a SELECT statement which may include duplicates. 1142 1143 A special subclass :class:`.DedupeColumnCollection` exists which instead 1144 maintains SQLAlchemy's older behavior of not allowing duplicates; this 1145 collection is used for schema level objects like :class:`_schema.Table` 1146 and 1147 :class:`.PrimaryKeyConstraint` where this deduping is helpful. The 1148 :class:`.DedupeColumnCollection` class also has additional mutation methods 1149 as the schema constructs have more use cases that require removal and 1150 replacement of columns. 1151 1152 .. versionchanged:: 1.4 :class:`_expression.ColumnCollection` 1153 now stores duplicate 1154 column keys as well as the same column in multiple positions. The 1155 :class:`.DedupeColumnCollection` class is added to maintain the 1156 former behavior in those cases where deduplication as well as 1157 additional replace/remove operations are needed. 1158 1159 1160 """ 1161 1162 __slots__ = "_collection", "_index", "_colset" 1163 1164 def __init__(self, columns=None): 1165 object.__setattr__(self, "_colset", set()) 1166 object.__setattr__(self, "_index", {}) 1167 object.__setattr__(self, "_collection", []) 1168 if columns: 1169 self._initial_populate(columns) 1170 1171 def _initial_populate(self, iter_): 1172 self._populate_separate_keys(iter_) 1173 1174 @property 1175 def _all_columns(self): 1176 return [col for (k, col) in self._collection] 1177 1178 def keys(self): 1179 """Return a sequence of string key names for all columns in this 1180 collection.""" 1181 return [k for (k, col) in self._collection] 1182 1183 def values(self): 1184 """Return a sequence of :class:`_sql.ColumnClause` or 1185 :class:`_schema.Column` objects for all columns in this 1186 collection.""" 1187 return [col for (k, col) in self._collection] 1188 1189 def items(self): 1190 """Return a sequence of (key, column) tuples for all columns in this 1191 collection each consisting of a string key name and a 1192 :class:`_sql.ColumnClause` or 1193 :class:`_schema.Column` object. 1194 """ 1195 1196 return list(self._collection) 1197 1198 def __bool__(self): 1199 return bool(self._collection) 1200 1201 def __len__(self): 1202 return len(self._collection) 1203 1204 def __iter__(self): 1205 # turn to a list first to maintain over a course of changes 1206 return iter([col for k, col in self._collection]) 1207 1208 def __getitem__(self, key): 1209 try: 1210 return self._index[key] 1211 except KeyError as err: 1212 if isinstance(key, util.int_types): 1213 util.raise_(IndexError(key), replace_context=err) 1214 else: 1215 raise 1216 1217 def __getattr__(self, key): 1218 try: 1219 return self._index[key] 1220 except KeyError as err: 1221 util.raise_(AttributeError(key), replace_context=err) 1222 1223 def __contains__(self, key): 1224 if key not in self._index: 1225 if not isinstance(key, util.string_types): 1226 raise exc.ArgumentError( 1227 "__contains__ requires a string argument" 1228 ) 1229 return False 1230 else: 1231 return True 1232 1233 def compare(self, other): 1234 """Compare this :class:`_expression.ColumnCollection` to another 1235 based on the names of the keys""" 1236 1237 for l, r in util.zip_longest(self, other): 1238 if l is not r: 1239 return False 1240 else: 1241 return True 1242 1243 def __eq__(self, other): 1244 return self.compare(other) 1245 1246 def get(self, key, default=None): 1247 """Get a :class:`_sql.ColumnClause` or :class:`_schema.Column` object 1248 based on a string key name from this 1249 :class:`_expression.ColumnCollection`.""" 1250 1251 if key in self._index: 1252 return self._index[key] 1253 else: 1254 return default 1255 1256 def __str__(self): 1257 return "%s(%s)" % ( 1258 self.__class__.__name__, 1259 ", ".join(str(c) for c in self), 1260 ) 1261 1262 def __setitem__(self, key, value): 1263 raise NotImplementedError() 1264 1265 def __delitem__(self, key): 1266 raise NotImplementedError() 1267 1268 def __setattr__(self, key, obj): 1269 raise NotImplementedError() 1270 1271 def clear(self): 1272 """Dictionary clear() is not implemented for 1273 :class:`_sql.ColumnCollection`.""" 1274 raise NotImplementedError() 1275 1276 def remove(self, column): 1277 """Dictionary remove() is not implemented for 1278 :class:`_sql.ColumnCollection`.""" 1279 raise NotImplementedError() 1280 1281 def update(self, iter_): 1282 """Dictionary update() is not implemented for 1283 :class:`_sql.ColumnCollection`.""" 1284 raise NotImplementedError() 1285 1286 __hash__ = None 1287 1288 def _populate_separate_keys(self, iter_): 1289 """populate from an iterator of (key, column)""" 1290 cols = list(iter_) 1291 self._collection[:] = cols 1292 self._colset.update(c for k, c in self._collection) 1293 self._index.update( 1294 (idx, c) for idx, (k, c) in enumerate(self._collection) 1295 ) 1296 self._index.update({k: col for k, col in reversed(self._collection)}) 1297 1298 def add(self, column, key=None): 1299 """Add a column to this :class:`_sql.ColumnCollection`. 1300 1301 .. note:: 1302 1303 This method is **not normally used by user-facing code**, as the 1304 :class:`_sql.ColumnCollection` is usually part of an existing 1305 object such as a :class:`_schema.Table`. To add a 1306 :class:`_schema.Column` to an existing :class:`_schema.Table` 1307 object, use the :meth:`_schema.Table.append_column` method. 1308 1309 """ 1310 if key is None: 1311 key = column.key 1312 1313 l = len(self._collection) 1314 self._collection.append((key, column)) 1315 self._colset.add(column) 1316 self._index[l] = column 1317 if key not in self._index: 1318 self._index[key] = column 1319 1320 def __getstate__(self): 1321 return {"_collection": self._collection, "_index": self._index} 1322 1323 def __setstate__(self, state): 1324 object.__setattr__(self, "_index", state["_index"]) 1325 object.__setattr__(self, "_collection", state["_collection"]) 1326 object.__setattr__( 1327 self, "_colset", {col for k, col in self._collection} 1328 ) 1329 1330 def contains_column(self, col): 1331 """Checks if a column object exists in this collection""" 1332 if col not in self._colset: 1333 if isinstance(col, util.string_types): 1334 raise exc.ArgumentError( 1335 "contains_column cannot be used with string arguments. " 1336 "Use ``col_name in table.c`` instead." 1337 ) 1338 return False 1339 else: 1340 return True 1341 1342 def as_immutable(self): 1343 """Return an "immutable" form of this 1344 :class:`_sql.ColumnCollection`.""" 1345 1346 return ImmutableColumnCollection(self) 1347 1348 def corresponding_column(self, column, require_embedded=False): 1349 """Given a :class:`_expression.ColumnElement`, return the exported 1350 :class:`_expression.ColumnElement` object from this 1351 :class:`_expression.ColumnCollection` 1352 which corresponds to that original :class:`_expression.ColumnElement` 1353 via a common 1354 ancestor column. 1355 1356 :param column: the target :class:`_expression.ColumnElement` 1357 to be matched. 1358 1359 :param require_embedded: only return corresponding columns for 1360 the given :class:`_expression.ColumnElement`, if the given 1361 :class:`_expression.ColumnElement` 1362 is actually present within a sub-element 1363 of this :class:`_expression.Selectable`. 1364 Normally the column will match if 1365 it merely shares a common ancestor with one of the exported 1366 columns of this :class:`_expression.Selectable`. 1367 1368 .. seealso:: 1369 1370 :meth:`_expression.Selectable.corresponding_column` 1371 - invokes this method 1372 against the collection returned by 1373 :attr:`_expression.Selectable.exported_columns`. 1374 1375 .. versionchanged:: 1.4 the implementation for ``corresponding_column`` 1376 was moved onto the :class:`_expression.ColumnCollection` itself. 1377 1378 """ 1379 1380 def embedded(expanded_proxy_set, target_set): 1381 for t in target_set.difference(expanded_proxy_set): 1382 if not set(_expand_cloned([t])).intersection( 1383 expanded_proxy_set 1384 ): 1385 return False 1386 return True 1387 1388 # don't dig around if the column is locally present 1389 if column in self._colset: 1390 return column 1391 col, intersect = None, None 1392 target_set = column.proxy_set 1393 cols = [c for (k, c) in self._collection] 1394 for c in cols: 1395 expanded_proxy_set = set(_expand_cloned(c.proxy_set)) 1396 i = target_set.intersection(expanded_proxy_set) 1397 if i and ( 1398 not require_embedded 1399 or embedded(expanded_proxy_set, target_set) 1400 ): 1401 if col is None: 1402 1403 # no corresponding column yet, pick this one. 1404 1405 col, intersect = c, i 1406 elif len(i) > len(intersect): 1407 1408 # 'c' has a larger field of correspondence than 1409 # 'col'. i.e. selectable.c.a1_x->a1.c.x->table.c.x 1410 # matches a1.c.x->table.c.x better than 1411 # selectable.c.x->table.c.x does. 1412 1413 col, intersect = c, i 1414 elif i == intersect: 1415 # they have the same field of correspondence. see 1416 # which proxy_set has fewer columns in it, which 1417 # indicates a closer relationship with the root 1418 # column. Also take into account the "weight" 1419 # attribute which CompoundSelect() uses to give 1420 # higher precedence to columns based on vertical 1421 # position in the compound statement, and discard 1422 # columns that have no reference to the target 1423 # column (also occurs with CompoundSelect) 1424 1425 col_distance = util.reduce( 1426 operator.add, 1427 [ 1428 sc._annotations.get("weight", 1) 1429 for sc in col._uncached_proxy_set() 1430 if sc.shares_lineage(column) 1431 ], 1432 ) 1433 c_distance = util.reduce( 1434 operator.add, 1435 [ 1436 sc._annotations.get("weight", 1) 1437 for sc in c._uncached_proxy_set() 1438 if sc.shares_lineage(column) 1439 ], 1440 ) 1441 if c_distance < col_distance: 1442 col, intersect = c, i 1443 return col 1444 1445 1446class DedupeColumnCollection(ColumnCollection): 1447 """A :class:`_expression.ColumnCollection` 1448 that maintains deduplicating behavior. 1449 1450 This is useful by schema level objects such as :class:`_schema.Table` and 1451 :class:`.PrimaryKeyConstraint`. The collection includes more 1452 sophisticated mutator methods as well to suit schema objects which 1453 require mutable column collections. 1454 1455 .. versionadded:: 1.4 1456 1457 """ 1458 1459 def add(self, column, key=None): 1460 1461 if key is not None and column.key != key: 1462 raise exc.ArgumentError( 1463 "DedupeColumnCollection requires columns be under " 1464 "the same key as their .key" 1465 ) 1466 key = column.key 1467 1468 if key is None: 1469 raise exc.ArgumentError( 1470 "Can't add unnamed column to column collection" 1471 ) 1472 1473 if key in self._index: 1474 1475 existing = self._index[key] 1476 1477 if existing is column: 1478 return 1479 1480 self.replace(column) 1481 1482 # pop out memoized proxy_set as this 1483 # operation may very well be occurring 1484 # in a _make_proxy operation 1485 util.memoized_property.reset(column, "proxy_set") 1486 else: 1487 l = len(self._collection) 1488 self._collection.append((key, column)) 1489 self._colset.add(column) 1490 self._index[l] = column 1491 self._index[key] = column 1492 1493 def _populate_separate_keys(self, iter_): 1494 """populate from an iterator of (key, column)""" 1495 cols = list(iter_) 1496 1497 replace_col = [] 1498 for k, col in cols: 1499 if col.key != k: 1500 raise exc.ArgumentError( 1501 "DedupeColumnCollection requires columns be under " 1502 "the same key as their .key" 1503 ) 1504 if col.name in self._index and col.key != col.name: 1505 replace_col.append(col) 1506 elif col.key in self._index: 1507 replace_col.append(col) 1508 else: 1509 self._index[k] = col 1510 self._collection.append((k, col)) 1511 self._colset.update(c for (k, c) in self._collection) 1512 self._index.update( 1513 (idx, c) for idx, (k, c) in enumerate(self._collection) 1514 ) 1515 for col in replace_col: 1516 self.replace(col) 1517 1518 def extend(self, iter_): 1519 self._populate_separate_keys((col.key, col) for col in iter_) 1520 1521 def remove(self, column): 1522 if column not in self._colset: 1523 raise ValueError( 1524 "Can't remove column %r; column is not in this collection" 1525 % column 1526 ) 1527 del self._index[column.key] 1528 self._colset.remove(column) 1529 self._collection[:] = [ 1530 (k, c) for (k, c) in self._collection if c is not column 1531 ] 1532 self._index.update( 1533 {idx: col for idx, (k, col) in enumerate(self._collection)} 1534 ) 1535 # delete higher index 1536 del self._index[len(self._collection)] 1537 1538 def replace(self, column): 1539 """add the given column to this collection, removing unaliased 1540 versions of this column as well as existing columns with the 1541 same key. 1542 1543 e.g.:: 1544 1545 t = Table('sometable', metadata, Column('col1', Integer)) 1546 t.columns.replace(Column('col1', Integer, key='columnone')) 1547 1548 will remove the original 'col1' from the collection, and add 1549 the new column under the name 'columnname'. 1550 1551 Used by schema.Column to override columns during table reflection. 1552 1553 """ 1554 1555 remove_col = set() 1556 # remove up to two columns based on matches of name as well as key 1557 if column.name in self._index and column.key != column.name: 1558 other = self._index[column.name] 1559 if other.name == other.key: 1560 remove_col.add(other) 1561 1562 if column.key in self._index: 1563 remove_col.add(self._index[column.key]) 1564 1565 new_cols = [] 1566 replaced = False 1567 for k, col in self._collection: 1568 if col in remove_col: 1569 if not replaced: 1570 replaced = True 1571 new_cols.append((column.key, column)) 1572 else: 1573 new_cols.append((k, col)) 1574 1575 if remove_col: 1576 self._colset.difference_update(remove_col) 1577 1578 if not replaced: 1579 new_cols.append((column.key, column)) 1580 1581 self._colset.add(column) 1582 self._collection[:] = new_cols 1583 1584 self._index.clear() 1585 self._index.update( 1586 {idx: col for idx, (k, col) in enumerate(self._collection)} 1587 ) 1588 self._index.update(self._collection) 1589 1590 1591class ImmutableColumnCollection(util.ImmutableContainer, ColumnCollection): 1592 __slots__ = ("_parent",) 1593 1594 def __init__(self, collection): 1595 object.__setattr__(self, "_parent", collection) 1596 object.__setattr__(self, "_colset", collection._colset) 1597 object.__setattr__(self, "_index", collection._index) 1598 object.__setattr__(self, "_collection", collection._collection) 1599 1600 def __getstate__(self): 1601 return {"_parent": self._parent} 1602 1603 def __setstate__(self, state): 1604 parent = state["_parent"] 1605 self.__init__(parent) 1606 1607 add = extend = remove = util.ImmutableContainer._immutable 1608 1609 1610class ColumnSet(util.ordered_column_set): 1611 def contains_column(self, col): 1612 return col in self 1613 1614 def extend(self, cols): 1615 for col in cols: 1616 self.add(col) 1617 1618 def __add__(self, other): 1619 return list(self) + list(other) 1620 1621 def __eq__(self, other): 1622 l = [] 1623 for c in other: 1624 for local in self: 1625 if c.shares_lineage(local): 1626 l.append(c == local) 1627 return elements.and_(*l) 1628 1629 def __hash__(self): 1630 return hash(tuple(x for x in self)) 1631 1632 1633def _bind_or_error(schemaitem, msg=None): 1634 1635 util.warn_deprecated_20( 1636 "The ``bind`` argument for schema methods that invoke SQL " 1637 "against an engine or connection will be required in SQLAlchemy 2.0." 1638 ) 1639 bind = schemaitem.bind 1640 if not bind: 1641 name = schemaitem.__class__.__name__ 1642 label = getattr( 1643 schemaitem, "fullname", getattr(schemaitem, "name", None) 1644 ) 1645 if label: 1646 item = "%s object %r" % (name, label) 1647 else: 1648 item = "%s object" % name 1649 if msg is None: 1650 msg = ( 1651 "%s is not bound to an Engine or Connection. " 1652 "Execution can not proceed without a database to execute " 1653 "against." % item 1654 ) 1655 raise exc.UnboundExecutionError(msg) 1656 return bind 1657 1658 1659def _entity_namespace(entity): 1660 """Return the nearest .entity_namespace for the given entity. 1661 1662 If not immediately available, does an iterate to find a sub-element 1663 that has one, if any. 1664 1665 """ 1666 try: 1667 return entity.entity_namespace 1668 except AttributeError: 1669 for elem in visitors.iterate(entity): 1670 if hasattr(elem, "entity_namespace"): 1671 return elem.entity_namespace 1672 else: 1673 raise 1674 1675 1676def _entity_namespace_key(entity, key, default=NO_ARG): 1677 """Return an entry from an entity_namespace. 1678 1679 1680 Raises :class:`_exc.InvalidRequestError` rather than attribute error 1681 on not found. 1682 1683 """ 1684 1685 try: 1686 ns = _entity_namespace(entity) 1687 if default is not NO_ARG: 1688 return getattr(ns, key, default) 1689 else: 1690 return getattr(ns, key) 1691 except AttributeError as err: 1692 util.raise_( 1693 exc.InvalidRequestError( 1694 'Entity namespace for "%s" has no property "%s"' 1695 % (entity, key) 1696 ), 1697 replace_context=err, 1698 ) 1699