123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339 |
- # engine/base.py
- # Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
- # <see AUTHORS file>
- #
- # This module is part of SQLAlchemy and is released under
- # the MIT License: https://www.opensource.org/licenses/mit-license.php
- from __future__ import with_statement
- import contextlib
- import sys
- from .interfaces import Connectable
- from .interfaces import ExceptionContext
- from .util import _distill_params
- from .util import _distill_params_20
- from .util import TransactionalContext
- from .. import exc
- from .. import inspection
- from .. import log
- from .. import util
- from ..sql import compiler
- from ..sql import util as sql_util
- """Defines :class:`_engine.Connection` and :class:`_engine.Engine`.
- """
- _EMPTY_EXECUTION_OPTS = util.immutabledict()
- class Connection(Connectable):
- """Provides high-level functionality for a wrapped DB-API connection.
- **This is the SQLAlchemy 1.x.x version** of the :class:`_engine.Connection`
- class. For the :term:`2.0 style` version, which features some API
- differences, see :class:`_future.Connection`.
- The :class:`_engine.Connection` object is procured by calling
- the :meth:`_engine.Engine.connect` method of the :class:`_engine.Engine`
- object, and provides services for execution of SQL statements as well
- as transaction control.
- The Connection object is **not** thread-safe. While a Connection can be
- shared among threads using properly synchronized access, it is still
- possible that the underlying DBAPI connection may not support shared
- access between threads. Check the DBAPI documentation for details.
- The Connection object represents a single DBAPI connection checked out
- from the connection pool. In this state, the connection pool has no affect
- upon the connection, including its expiration or timeout state. For the
- connection pool to properly manage connections, connections should be
- returned to the connection pool (i.e. ``connection.close()``) whenever the
- connection is not in use.
- .. index::
- single: thread safety; Connection
- """
- _is_future = False
- _sqla_logger_namespace = "sqlalchemy.engine.Connection"
- # used by sqlalchemy.engine.util.TransactionalContext
- _trans_context_manager = None
- def __init__(
- self,
- engine,
- connection=None,
- close_with_result=False,
- _branch_from=None,
- _execution_options=None,
- _dispatch=None,
- _has_events=None,
- _allow_revalidate=True,
- ):
- """Construct a new Connection."""
- self.engine = engine
- self.dialect = engine.dialect
- self.__branch_from = _branch_from
- if _branch_from:
- # branching is always "from" the root connection
- assert _branch_from.__branch_from is None
- self._dbapi_connection = connection
- self._execution_options = _execution_options
- self._echo = _branch_from._echo
- self.should_close_with_result = False
- self.dispatch = _dispatch
- self._has_events = _branch_from._has_events
- else:
- self._dbapi_connection = (
- connection
- if connection is not None
- else engine.raw_connection()
- )
- self._transaction = self._nested_transaction = None
- self.__savepoint_seq = 0
- self.__in_begin = False
- self.should_close_with_result = close_with_result
- self.__can_reconnect = _allow_revalidate
- self._echo = self.engine._should_log_info()
- if _has_events is None:
- # if _has_events is sent explicitly as False,
- # then don't join the dispatch of the engine; we don't
- # want to handle any of the engine's events in that case.
- self.dispatch = self.dispatch._join(engine.dispatch)
- self._has_events = _has_events or (
- _has_events is None and engine._has_events
- )
- assert not _execution_options
- self._execution_options = engine._execution_options
- if self._has_events or self.engine._has_events:
- self.dispatch.engine_connect(self, _branch_from is not None)
- @util.memoized_property
- def _message_formatter(self):
- if "logging_token" in self._execution_options:
- token = self._execution_options["logging_token"]
- return lambda msg: "[%s] %s" % (token, msg)
- else:
- return None
- def _log_info(self, message, *arg, **kw):
- fmt = self._message_formatter
- if fmt:
- message = fmt(message)
- self.engine.logger.info(message, *arg, **kw)
- def _log_debug(self, message, *arg, **kw):
- fmt = self._message_formatter
- if fmt:
- message = fmt(message)
- self.engine.logger.debug(message, *arg, **kw)
- @property
- def _schema_translate_map(self):
- return self._execution_options.get("schema_translate_map", None)
- def schema_for_object(self, obj):
- """Return the schema name for the given schema item taking into
- account current schema translate map.
- """
- name = obj.schema
- schema_translate_map = self._execution_options.get(
- "schema_translate_map", None
- )
- if (
- schema_translate_map
- and name in schema_translate_map
- and obj._use_schema_map
- ):
- return schema_translate_map[name]
- else:
- return name
- def _branch(self):
- """Return a new Connection which references this Connection's
- engine and connection; but does not have close_with_result enabled,
- and also whose close() method does nothing.
- .. deprecated:: 1.4 the "branching" concept will be removed in
- SQLAlchemy 2.0 as well as the "Connection.connect()" method which
- is the only consumer for this.
- The Core uses this very sparingly, only in the case of
- custom SQL default functions that are to be INSERTed as the
- primary key of a row where we need to get the value back, so we have
- to invoke it distinctly - this is a very uncommon case.
- Userland code accesses _branch() when the connect()
- method is called. The branched connection
- acts as much as possible like the parent, except that it stays
- connected when a close() event occurs.
- """
- return self.engine._connection_cls(
- self.engine,
- self._dbapi_connection,
- _branch_from=self.__branch_from if self.__branch_from else self,
- _execution_options=self._execution_options,
- _has_events=self._has_events,
- _dispatch=self.dispatch,
- )
- def _generate_for_options(self):
- """define connection method chaining behavior for execution_options"""
- if self._is_future:
- return self
- else:
- c = self.__class__.__new__(self.__class__)
- c.__dict__ = self.__dict__.copy()
- return c
- def __enter__(self):
- return self
- def __exit__(self, type_, value, traceback):
- self.close()
- def execution_options(self, **opt):
- r""" Set non-SQL options for the connection which take effect
- during execution.
- For a "future" style connection, this method returns this same
- :class:`_future.Connection` object with the new options added.
- For a legacy connection, this method returns a copy of this
- :class:`_engine.Connection` which references the same underlying DBAPI
- connection, but also defines the given execution options which will
- take effect for a call to
- :meth:`execute`. As the new :class:`_engine.Connection` references the
- same underlying resource, it's usually a good idea to ensure that
- the copies will be discarded immediately, which is implicit if used
- as in::
- result = connection.execution_options(stream_results=True).\
- execute(stmt)
- Note that any key/value can be passed to
- :meth:`_engine.Connection.execution_options`,
- and it will be stored in the
- ``_execution_options`` dictionary of the :class:`_engine.Connection`.
- It
- is suitable for usage by end-user schemes to communicate with
- event listeners, for example.
- The keywords that are currently recognized by SQLAlchemy itself
- include all those listed under :meth:`.Executable.execution_options`,
- as well as others that are specific to :class:`_engine.Connection`.
- :param autocommit: Available on: Connection, statement.
- When True, a COMMIT will be invoked after execution
- when executed in 'autocommit' mode, i.e. when an explicit
- transaction is not begun on the connection. Note that this
- is **library level, not DBAPI level autocommit**. The DBAPI
- connection will remain in a real transaction unless the
- "AUTOCOMMIT" isolation level is used.
- .. deprecated:: 1.4 The "autocommit" execution option is deprecated
- and will be removed in SQLAlchemy 2.0. See
- :ref:`migration_20_autocommit` for discussion.
- :param compiled_cache: Available on: Connection.
- A dictionary where :class:`.Compiled` objects
- will be cached when the :class:`_engine.Connection`
- compiles a clause
- expression into a :class:`.Compiled` object. This dictionary will
- supersede the statement cache that may be configured on the
- :class:`_engine.Engine` itself. If set to None, caching
- is disabled, even if the engine has a configured cache size.
- Note that the ORM makes use of its own "compiled" caches for
- some operations, including flush operations. The caching
- used by the ORM internally supersedes a cache dictionary
- specified here.
- :param logging_token: Available on: :class:`_engine.Connection`,
- :class:`_engine.Engine`.
- Adds the specified string token surrounded by brackets in log
- messages logged by the connection, i.e. the logging that's enabled
- either via the :paramref:`_sa.create_engine.echo` flag or via the
- ``logging.getLogger("sqlalchemy.engine")`` logger. This allows a
- per-connection or per-sub-engine token to be available which is
- useful for debugging concurrent connection scenarios.
- .. versionadded:: 1.4.0b2
- .. seealso::
- :ref:`dbengine_logging_tokens` - usage example
- :paramref:`_sa.create_engine.logging_name` - adds a name to the
- name used by the Python logger object itself.
- :param isolation_level: Available on: :class:`_engine.Connection`.
- Set the transaction isolation level for the lifespan of this
- :class:`_engine.Connection` object.
- Valid values include those string
- values accepted by the :paramref:`_sa.create_engine.isolation_level`
- parameter passed to :func:`_sa.create_engine`. These levels are
- semi-database specific; see individual dialect documentation for
- valid levels.
- The isolation level option applies the isolation level by emitting
- statements on the DBAPI connection, and **necessarily affects the
- original Connection object overall**, not just the copy that is
- returned by the call to :meth:`_engine.Connection.execution_options`
- method. The isolation level will remain at the given setting until
- the DBAPI connection itself is returned to the connection pool, i.e.
- the :meth:`_engine.Connection.close` method on the original
- :class:`_engine.Connection` is called,
- where an event handler will emit
- additional statements on the DBAPI connection in order to revert the
- isolation level change.
- .. warning:: The ``isolation_level`` execution option should
- **not** be used when a transaction is already established, that
- is, the :meth:`_engine.Connection.begin`
- method or similar has been
- called. A database cannot change the isolation level on a
- transaction in progress, and different DBAPIs and/or
- SQLAlchemy dialects may implicitly roll back or commit
- the transaction, or not affect the connection at all.
- .. note:: The ``isolation_level`` execution option is implicitly
- reset if the :class:`_engine.Connection` is invalidated, e.g. via
- the :meth:`_engine.Connection.invalidate` method, or if a
- disconnection error occurs. The new connection produced after
- the invalidation will not have the isolation level re-applied
- to it automatically.
- .. seealso::
- :paramref:`_sa.create_engine.isolation_level`
- - set per :class:`_engine.Engine` isolation level
- :meth:`_engine.Connection.get_isolation_level`
- - view current level
- :ref:`SQLite Transaction Isolation <sqlite_isolation_level>`
- :ref:`PostgreSQL Transaction Isolation <postgresql_isolation_level>`
- :ref:`MySQL Transaction Isolation <mysql_isolation_level>`
- :ref:`SQL Server Transaction Isolation <mssql_isolation_level>`
- :ref:`session_transaction_isolation` - for the ORM
- :param no_parameters: When ``True``, if the final parameter
- list or dictionary is totally empty, will invoke the
- statement on the cursor as ``cursor.execute(statement)``,
- not passing the parameter collection at all.
- Some DBAPIs such as psycopg2 and mysql-python consider
- percent signs as significant only when parameters are
- present; this option allows code to generate SQL
- containing percent signs (and possibly other characters)
- that is neutral regarding whether it's executed by the DBAPI
- or piped into a script that's later invoked by
- command line tools.
- :param stream_results: Available on: Connection, statement.
- Indicate to the dialect that results should be
- "streamed" and not pre-buffered, if possible. This is a limitation
- of many DBAPIs. The flag is currently understood within a subset
- of dialects within the PostgreSQL and MySQL categories, and
- may be supported by other third party dialects as well.
- .. seealso::
- :ref:`engine_stream_results`
- :param schema_translate_map: Available on: Connection, Engine.
- A dictionary mapping schema names to schema names, that will be
- applied to the :paramref:`_schema.Table.schema` element of each
- :class:`_schema.Table`
- encountered when SQL or DDL expression elements
- are compiled into strings; the resulting schema name will be
- converted based on presence in the map of the original name.
- .. versionadded:: 1.1
- .. seealso::
- :ref:`schema_translating`
- .. seealso::
- :meth:`_engine.Engine.execution_options`
- :meth:`.Executable.execution_options`
- :meth:`_engine.Connection.get_execution_options`
- """ # noqa
- c = self._generate_for_options()
- c._execution_options = c._execution_options.union(opt)
- if self._has_events or self.engine._has_events:
- self.dispatch.set_connection_execution_options(c, opt)
- self.dialect.set_connection_execution_options(c, opt)
- return c
- def get_execution_options(self):
- """Get the non-SQL options which will take effect during execution.
- .. versionadded:: 1.3
- .. seealso::
- :meth:`_engine.Connection.execution_options`
- """
- return self._execution_options
- @property
- def closed(self):
- """Return True if this connection is closed."""
- # note this is independent for a "branched" connection vs.
- # the base
- return self._dbapi_connection is None and not self.__can_reconnect
- @property
- def invalidated(self):
- """Return True if this connection was invalidated."""
- # prior to 1.4, "invalid" was stored as a state independent of
- # "closed", meaning an invalidated connection could be "closed",
- # the _dbapi_connection would be None and closed=True, yet the
- # "invalid" flag would stay True. This meant that there were
- # three separate states (open/valid, closed/valid, closed/invalid)
- # when there is really no reason for that; a connection that's
- # "closed" does not need to be "invalid". So the state is now
- # represented by the two facts alone.
- if self.__branch_from:
- return self.__branch_from.invalidated
- return self._dbapi_connection is None and not self.closed
- @property
- def connection(self):
- """The underlying DB-API connection managed by this Connection.
- This is a SQLAlchemy connection-pool proxied connection
- which then has the attribute
- :attr:`_pool._ConnectionFairy.dbapi_connection` that refers to the
- actual driver connection.
- .. seealso::
- :ref:`dbapi_connections`
- """
- if self._dbapi_connection is None:
- try:
- return self._revalidate_connection()
- except (exc.PendingRollbackError, exc.ResourceClosedError):
- raise
- except BaseException as e:
- self._handle_dbapi_exception(e, None, None, None, None)
- else:
- return self._dbapi_connection
- def get_isolation_level(self):
- """Return the current isolation level assigned to this
- :class:`_engine.Connection`.
- This will typically be the default isolation level as determined
- by the dialect, unless if the
- :paramref:`.Connection.execution_options.isolation_level`
- feature has been used to alter the isolation level on a
- per-:class:`_engine.Connection` basis.
- This attribute will typically perform a live SQL operation in order
- to procure the current isolation level, so the value returned is the
- actual level on the underlying DBAPI connection regardless of how
- this state was set. Compare to the
- :attr:`_engine.Connection.default_isolation_level` accessor
- which returns the dialect-level setting without performing a SQL
- query.
- .. versionadded:: 0.9.9
- .. seealso::
- :attr:`_engine.Connection.default_isolation_level`
- - view default level
- :paramref:`_sa.create_engine.isolation_level`
- - set per :class:`_engine.Engine` isolation level
- :paramref:`.Connection.execution_options.isolation_level`
- - set per :class:`_engine.Connection` isolation level
- """
- try:
- return self.dialect.get_isolation_level(self.connection)
- except BaseException as e:
- self._handle_dbapi_exception(e, None, None, None, None)
- @property
- def default_isolation_level(self):
- """The default isolation level assigned to this
- :class:`_engine.Connection`.
- This is the isolation level setting that the
- :class:`_engine.Connection`
- has when first procured via the :meth:`_engine.Engine.connect` method.
- This level stays in place until the
- :paramref:`.Connection.execution_options.isolation_level` is used
- to change the setting on a per-:class:`_engine.Connection` basis.
- Unlike :meth:`_engine.Connection.get_isolation_level`,
- this attribute is set
- ahead of time from the first connection procured by the dialect,
- so SQL query is not invoked when this accessor is called.
- .. versionadded:: 0.9.9
- .. seealso::
- :meth:`_engine.Connection.get_isolation_level`
- - view current level
- :paramref:`_sa.create_engine.isolation_level`
- - set per :class:`_engine.Engine` isolation level
- :paramref:`.Connection.execution_options.isolation_level`
- - set per :class:`_engine.Connection` isolation level
- """
- return self.dialect.default_isolation_level
- def _invalid_transaction(self):
- if self.invalidated:
- raise exc.PendingRollbackError(
- "Can't reconnect until invalid %stransaction is rolled "
- "back."
- % (
- "savepoint "
- if self._nested_transaction is not None
- else ""
- ),
- code="8s2b",
- )
- else:
- assert not self._is_future
- raise exc.PendingRollbackError(
- "This connection is on an inactive %stransaction. "
- "Please rollback() fully before proceeding."
- % (
- "savepoint "
- if self._nested_transaction is not None
- else ""
- ),
- code="8s2a",
- )
- def _revalidate_connection(self):
- if self.__branch_from:
- return self.__branch_from._revalidate_connection()
- if self.__can_reconnect and self.invalidated:
- if self._transaction is not None:
- self._invalid_transaction()
- self._dbapi_connection = self.engine.raw_connection(
- _connection=self
- )
- return self._dbapi_connection
- raise exc.ResourceClosedError("This Connection is closed")
- @property
- def _still_open_and_dbapi_connection_is_valid(self):
- return self._dbapi_connection is not None and getattr(
- self._dbapi_connection, "is_valid", False
- )
- @property
- def info(self):
- """Info dictionary associated with the underlying DBAPI connection
- referred to by this :class:`_engine.Connection`, allowing user-defined
- data to be associated with the connection.
- The data here will follow along with the DBAPI connection including
- after it is returned to the connection pool and used again
- in subsequent instances of :class:`_engine.Connection`.
- """
- return self.connection.info
- @util.deprecated_20(":meth:`.Connection.connect`")
- def connect(self, close_with_result=False):
- """Returns a branched version of this :class:`_engine.Connection`.
- The :meth:`_engine.Connection.close` method on the returned
- :class:`_engine.Connection` can be called and this
- :class:`_engine.Connection` will remain open.
- This method provides usage symmetry with
- :meth:`_engine.Engine.connect`, including for usage
- with context managers.
- """
- return self._branch()
- def invalidate(self, exception=None):
- """Invalidate the underlying DBAPI connection associated with
- this :class:`_engine.Connection`.
- An attempt will be made to close the underlying DBAPI connection
- immediately; however if this operation fails, the error is logged
- but not raised. The connection is then discarded whether or not
- close() succeeded.
- Upon the next use (where "use" typically means using the
- :meth:`_engine.Connection.execute` method or similar),
- this :class:`_engine.Connection` will attempt to
- procure a new DBAPI connection using the services of the
- :class:`_pool.Pool` as a source of connectivity (e.g.
- a "reconnection").
- If a transaction was in progress (e.g. the
- :meth:`_engine.Connection.begin` method has been called) when
- :meth:`_engine.Connection.invalidate` method is called, at the DBAPI
- level all state associated with this transaction is lost, as
- the DBAPI connection is closed. The :class:`_engine.Connection`
- will not allow a reconnection to proceed until the
- :class:`.Transaction` object is ended, by calling the
- :meth:`.Transaction.rollback` method; until that point, any attempt at
- continuing to use the :class:`_engine.Connection` will raise an
- :class:`~sqlalchemy.exc.InvalidRequestError`.
- This is to prevent applications from accidentally
- continuing an ongoing transactional operations despite the
- fact that the transaction has been lost due to an
- invalidation.
- The :meth:`_engine.Connection.invalidate` method,
- just like auto-invalidation,
- will at the connection pool level invoke the
- :meth:`_events.PoolEvents.invalidate` event.
- :param exception: an optional ``Exception`` instance that's the
- reason for the invalidation. is passed along to event handlers
- and logging functions.
- .. seealso::
- :ref:`pool_connection_invalidation`
- """
- if self.__branch_from:
- return self.__branch_from.invalidate(exception=exception)
- if self.invalidated:
- return
- if self.closed:
- raise exc.ResourceClosedError("This Connection is closed")
- if self._still_open_and_dbapi_connection_is_valid:
- self._dbapi_connection.invalidate(exception)
- self._dbapi_connection = None
- def detach(self):
- """Detach the underlying DB-API connection from its connection pool.
- E.g.::
- with engine.connect() as conn:
- conn.detach()
- conn.execute(text("SET search_path TO schema1, schema2"))
- # work with connection
- # connection is fully closed (since we used "with:", can
- # also call .close())
- This :class:`_engine.Connection` instance will remain usable.
- When closed
- (or exited from a context manager context as above),
- the DB-API connection will be literally closed and not
- returned to its originating pool.
- This method can be used to insulate the rest of an application
- from a modified state on a connection (such as a transaction
- isolation level or similar).
- """
- self._dbapi_connection.detach()
- def _autobegin(self):
- self.begin()
- def begin(self):
- """Begin a transaction and return a transaction handle.
- The returned object is an instance of :class:`.Transaction`.
- This object represents the "scope" of the transaction,
- which completes when either the :meth:`.Transaction.rollback`
- or :meth:`.Transaction.commit` method is called.
- .. tip::
- The :meth:`_engine.Connection.begin` method is invoked when using
- the :meth:`_engine.Engine.begin` context manager method as well.
- All documentation that refers to behaviors specific to the
- :meth:`_engine.Connection.begin` method also apply to use of the
- :meth:`_engine.Engine.begin` method.
- Legacy use: nested calls to :meth:`.begin` on the same
- :class:`_engine.Connection` will return new :class:`.Transaction`
- objects that represent an emulated transaction within the scope of the
- enclosing transaction, that is::
- trans = conn.begin() # outermost transaction
- trans2 = conn.begin() # "nested"
- trans2.commit() # does nothing
- trans.commit() # actually commits
- Calls to :meth:`.Transaction.commit` only have an effect
- when invoked via the outermost :class:`.Transaction` object, though the
- :meth:`.Transaction.rollback` method of any of the
- :class:`.Transaction` objects will roll back the
- transaction.
- .. tip::
- The above "nesting" behavior is a legacy behavior specific to
- :term:`1.x style` use and will be removed in SQLAlchemy 2.0. For
- notes on :term:`2.0 style` use, see
- :meth:`_future.Connection.begin`.
- .. seealso::
- :meth:`_engine.Connection.begin_nested` - use a SAVEPOINT
- :meth:`_engine.Connection.begin_twophase` -
- use a two phase /XID transaction
- :meth:`_engine.Engine.begin` - context manager available from
- :class:`_engine.Engine`
- """
- if self._is_future:
- assert not self.__branch_from
- elif self.__branch_from:
- return self.__branch_from.begin()
- if self.__in_begin:
- # for dialects that emit SQL within the process of
- # dialect.do_begin() or dialect.do_begin_twophase(), this
- # flag prevents "autobegin" from being emitted within that
- # process, while allowing self._transaction to remain at None
- # until it's complete.
- return
- elif self._transaction is None:
- self._transaction = RootTransaction(self)
- return self._transaction
- else:
- if self._is_future:
- raise exc.InvalidRequestError(
- "This connection has already initialized a SQLAlchemy "
- "Transaction() object via begin() or autobegin; can't "
- "call begin() here unless rollback() or commit() "
- "is called first."
- )
- else:
- return MarkerTransaction(self)
- def begin_nested(self):
- """Begin a nested transaction (i.e. SAVEPOINT) and return a
- transaction handle, assuming an outer transaction is already
- established.
- Nested transactions require SAVEPOINT support in the
- underlying database. Any transaction in the hierarchy may
- ``commit`` and ``rollback``, however the outermost transaction
- still controls the overall ``commit`` or ``rollback`` of the
- transaction of a whole.
- The legacy form of :meth:`_engine.Connection.begin_nested` method has
- alternate behaviors based on whether or not the
- :meth:`_engine.Connection.begin` method was called previously. If
- :meth:`_engine.Connection.begin` was not called, then this method will
- behave the same as the :meth:`_engine.Connection.begin` method and
- return a :class:`.RootTransaction` object that begins and commits a
- real transaction - **no savepoint is invoked**. If
- :meth:`_engine.Connection.begin` **has** been called, and a
- :class:`.RootTransaction` is already established, then this method
- returns an instance of :class:`.NestedTransaction` which will invoke
- and manage the scope of a SAVEPOINT.
- .. tip::
- The above mentioned behavior of
- :meth:`_engine.Connection.begin_nested` is a legacy behavior
- specific to :term:`1.x style` use. In :term:`2.0 style` use, the
- :meth:`_future.Connection.begin_nested` method instead autobegins
- the outer transaction that can be committed using
- "commit-as-you-go" style; see
- :meth:`_future.Connection.begin_nested` for migration details.
- .. versionchanged:: 1.4.13 The behavior of
- :meth:`_engine.Connection.begin_nested`
- as returning a :class:`.RootTransaction` if
- :meth:`_engine.Connection.begin` were not called has been restored
- as was the case in 1.3.x versions; in previous 1.4.x versions, an
- outer transaction would be "autobegun" but would not be committed.
- .. seealso::
- :meth:`_engine.Connection.begin`
- :meth:`_engine.Connection.begin_twophase`
- """
- if self._is_future:
- assert not self.__branch_from
- elif self.__branch_from:
- return self.__branch_from.begin_nested()
- if self._transaction is None:
- if not self._is_future:
- util.warn_deprecated_20(
- "Calling Connection.begin_nested() in 2.0 style use will "
- "return a NestedTransaction (SAVEPOINT) in all cases, "
- "that will not commit the outer transaction. For code "
- "that is cross-compatible between 1.x and 2.0 style use, "
- "ensure Connection.begin() is called before calling "
- "Connection.begin_nested()."
- )
- return self.begin()
- else:
- self._autobegin()
- return NestedTransaction(self)
- def begin_twophase(self, xid=None):
- """Begin a two-phase or XA transaction and return a transaction
- handle.
- The returned object is an instance of :class:`.TwoPhaseTransaction`,
- which in addition to the methods provided by
- :class:`.Transaction`, also provides a
- :meth:`~.TwoPhaseTransaction.prepare` method.
- :param xid: the two phase transaction id. If not supplied, a
- random id will be generated.
- .. seealso::
- :meth:`_engine.Connection.begin`
- :meth:`_engine.Connection.begin_twophase`
- """
- if self.__branch_from:
- return self.__branch_from.begin_twophase(xid=xid)
- if self._transaction is not None:
- raise exc.InvalidRequestError(
- "Cannot start a two phase transaction when a transaction "
- "is already in progress."
- )
- if xid is None:
- xid = self.engine.dialect.create_xid()
- return TwoPhaseTransaction(self, xid)
- def recover_twophase(self):
- return self.engine.dialect.do_recover_twophase(self)
- def rollback_prepared(self, xid, recover=False):
- self.engine.dialect.do_rollback_twophase(self, xid, recover=recover)
- def commit_prepared(self, xid, recover=False):
- self.engine.dialect.do_commit_twophase(self, xid, recover=recover)
- def in_transaction(self):
- """Return True if a transaction is in progress."""
- if self.__branch_from is not None:
- return self.__branch_from.in_transaction()
- return self._transaction is not None and self._transaction.is_active
- def in_nested_transaction(self):
- """Return True if a transaction is in progress."""
- if self.__branch_from is not None:
- return self.__branch_from.in_nested_transaction()
- return (
- self._nested_transaction is not None
- and self._nested_transaction.is_active
- )
- def _is_autocommit(self):
- return (
- self._execution_options.get("isolation_level", None)
- == "AUTOCOMMIT"
- )
- def get_transaction(self):
- """Return the current root transaction in progress, if any.
- .. versionadded:: 1.4
- """
- if self.__branch_from is not None:
- return self.__branch_from.get_transaction()
- return self._transaction
- def get_nested_transaction(self):
- """Return the current nested transaction in progress, if any.
- .. versionadded:: 1.4
- """
- if self.__branch_from is not None:
- return self.__branch_from.get_nested_transaction()
- return self._nested_transaction
- def _begin_impl(self, transaction):
- assert not self.__branch_from
- if self._echo:
- self._log_info("BEGIN (implicit)")
- self.__in_begin = True
- if self._has_events or self.engine._has_events:
- self.dispatch.begin(self)
- try:
- self.engine.dialect.do_begin(self.connection)
- except BaseException as e:
- self._handle_dbapi_exception(e, None, None, None, None)
- finally:
- self.__in_begin = False
- def _rollback_impl(self):
- assert not self.__branch_from
- if self._has_events or self.engine._has_events:
- self.dispatch.rollback(self)
- if self._still_open_and_dbapi_connection_is_valid:
- if self._echo:
- if self._is_autocommit():
- self._log_info(
- "ROLLBACK using DBAPI connection.rollback(), "
- "DBAPI should ignore due to autocommit mode"
- )
- else:
- self._log_info("ROLLBACK")
- try:
- self.engine.dialect.do_rollback(self.connection)
- except BaseException as e:
- self._handle_dbapi_exception(e, None, None, None, None)
- def _commit_impl(self, autocommit=False):
- assert not self.__branch_from
- # AUTOCOMMIT isolation-level is a dialect-specific concept, however
- # if a connection has this set as the isolation level, we can skip
- # the "autocommit" warning as the operation will do "autocommit"
- # in any case
- if autocommit and not self._is_autocommit():
- util.warn_deprecated_20(
- "The current statement is being autocommitted using "
- "implicit autocommit, which will be removed in "
- "SQLAlchemy 2.0. "
- "Use the .begin() method of Engine or Connection in order to "
- "use an explicit transaction for DML and DDL statements."
- )
- if self._has_events or self.engine._has_events:
- self.dispatch.commit(self)
- if self._echo:
- if self._is_autocommit():
- self._log_info(
- "COMMIT using DBAPI connection.commit(), "
- "DBAPI should ignore due to autocommit mode"
- )
- else:
- self._log_info("COMMIT")
- try:
- self.engine.dialect.do_commit(self.connection)
- except BaseException as e:
- self._handle_dbapi_exception(e, None, None, None, None)
- def _savepoint_impl(self, name=None):
- assert not self.__branch_from
- if self._has_events or self.engine._has_events:
- self.dispatch.savepoint(self, name)
- if name is None:
- self.__savepoint_seq += 1
- name = "sa_savepoint_%s" % self.__savepoint_seq
- if self._still_open_and_dbapi_connection_is_valid:
- self.engine.dialect.do_savepoint(self, name)
- return name
- def _rollback_to_savepoint_impl(self, name):
- assert not self.__branch_from
- if self._has_events or self.engine._has_events:
- self.dispatch.rollback_savepoint(self, name, None)
- if self._still_open_and_dbapi_connection_is_valid:
- self.engine.dialect.do_rollback_to_savepoint(self, name)
- def _release_savepoint_impl(self, name):
- assert not self.__branch_from
- if self._has_events or self.engine._has_events:
- self.dispatch.release_savepoint(self, name, None)
- if self._still_open_and_dbapi_connection_is_valid:
- self.engine.dialect.do_release_savepoint(self, name)
- def _begin_twophase_impl(self, transaction):
- assert not self.__branch_from
- if self._echo:
- self._log_info("BEGIN TWOPHASE (implicit)")
- if self._has_events or self.engine._has_events:
- self.dispatch.begin_twophase(self, transaction.xid)
- if self._still_open_and_dbapi_connection_is_valid:
- self.__in_begin = True
- try:
- self.engine.dialect.do_begin_twophase(self, transaction.xid)
- except BaseException as e:
- self._handle_dbapi_exception(e, None, None, None, None)
- finally:
- self.__in_begin = False
- def _prepare_twophase_impl(self, xid):
- assert not self.__branch_from
- if self._has_events or self.engine._has_events:
- self.dispatch.prepare_twophase(self, xid)
- if self._still_open_and_dbapi_connection_is_valid:
- assert isinstance(self._transaction, TwoPhaseTransaction)
- try:
- self.engine.dialect.do_prepare_twophase(self, xid)
- except BaseException as e:
- self._handle_dbapi_exception(e, None, None, None, None)
- def _rollback_twophase_impl(self, xid, is_prepared):
- assert not self.__branch_from
- if self._has_events or self.engine._has_events:
- self.dispatch.rollback_twophase(self, xid, is_prepared)
- if self._still_open_and_dbapi_connection_is_valid:
- assert isinstance(self._transaction, TwoPhaseTransaction)
- try:
- self.engine.dialect.do_rollback_twophase(
- self, xid, is_prepared
- )
- except BaseException as e:
- self._handle_dbapi_exception(e, None, None, None, None)
- def _commit_twophase_impl(self, xid, is_prepared):
- assert not self.__branch_from
- if self._has_events or self.engine._has_events:
- self.dispatch.commit_twophase(self, xid, is_prepared)
- if self._still_open_and_dbapi_connection_is_valid:
- assert isinstance(self._transaction, TwoPhaseTransaction)
- try:
- self.engine.dialect.do_commit_twophase(self, xid, is_prepared)
- except BaseException as e:
- self._handle_dbapi_exception(e, None, None, None, None)
- def _autorollback(self):
- if self.__branch_from:
- self.__branch_from._autorollback()
- if not self.in_transaction():
- self._rollback_impl()
- def _warn_for_legacy_exec_format(self):
- util.warn_deprecated_20(
- "The connection.execute() method in "
- "SQLAlchemy 2.0 will accept parameters as a single "
- "dictionary or a "
- "single sequence of dictionaries only. "
- "Parameters passed as keyword arguments, tuples or positionally "
- "oriented dictionaries and/or tuples "
- "will no longer be accepted."
- )
- def close(self):
- """Close this :class:`_engine.Connection`.
- This results in a release of the underlying database
- resources, that is, the DBAPI connection referenced
- internally. The DBAPI connection is typically restored
- back to the connection-holding :class:`_pool.Pool` referenced
- by the :class:`_engine.Engine` that produced this
- :class:`_engine.Connection`. Any transactional state present on
- the DBAPI connection is also unconditionally released via
- the DBAPI connection's ``rollback()`` method, regardless
- of any :class:`.Transaction` object that may be
- outstanding with regards to this :class:`_engine.Connection`.
- After :meth:`_engine.Connection.close` is called, the
- :class:`_engine.Connection` is permanently in a closed state,
- and will allow no further operations.
- """
- if self.__branch_from:
- assert not self._is_future
- util.warn_deprecated_20(
- "The .close() method on a so-called 'branched' connection is "
- "deprecated as of 1.4, as are 'branched' connections overall, "
- "and will be removed in a future release. If this is a "
- "default-handling function, don't close the connection."
- )
- self._dbapi_connection = None
- self.__can_reconnect = False
- return
- if self._transaction:
- self._transaction.close()
- skip_reset = True
- else:
- skip_reset = False
- if self._dbapi_connection is not None:
- conn = self._dbapi_connection
- # as we just closed the transaction, close the connection
- # pool connection without doing an additional reset
- if skip_reset:
- conn._close_no_reset()
- else:
- conn.close()
- # There is a slight chance that conn.close() may have
- # triggered an invalidation here in which case
- # _dbapi_connection would already be None, however usually
- # it will be non-None here and in a "closed" state.
- self._dbapi_connection = None
- self.__can_reconnect = False
- def scalar(self, object_, *multiparams, **params):
- """Executes and returns the first column of the first row.
- The underlying result/cursor is closed after execution.
- """
- return self.execute(object_, *multiparams, **params).scalar()
- def scalars(self, object_, *multiparams, **params):
- """Executes and returns a scalar result set, which yields scalar values
- from the first column of each row.
- This method is equivalent to calling :meth:`_engine.Connection.execute`
- to receive a :class:`_result.Result` object, then invoking the
- :meth:`_result.Result.scalars` method to produce a
- :class:`_result.ScalarResult` instance.
- :return: a :class:`_result.ScalarResult`
- .. versionadded:: 1.4.24
- """
- return self.execute(object_, *multiparams, **params).scalars()
- def execute(self, statement, *multiparams, **params):
- r"""Executes a SQL statement construct and returns a
- :class:`_engine.CursorResult`.
- :param statement: The statement to be executed. May be
- one of:
- * a plain string (deprecated)
- * any :class:`_expression.ClauseElement` construct that is also
- a subclass of :class:`.Executable`, such as a
- :func:`_expression.select` construct
- * a :class:`.FunctionElement`, such as that generated
- by :data:`.func`, will be automatically wrapped in
- a SELECT statement, which is then executed.
- * a :class:`.DDLElement` object
- * a :class:`.DefaultGenerator` object
- * a :class:`.Compiled` object
- .. deprecated:: 2.0 passing a string to
- :meth:`_engine.Connection.execute` is
- deprecated and will be removed in version 2.0. Use the
- :func:`_expression.text` construct with
- :meth:`_engine.Connection.execute`, or the
- :meth:`_engine.Connection.exec_driver_sql`
- method to invoke a driver-level
- SQL string.
- :param \*multiparams/\**params: represent bound parameter
- values to be used in the execution. Typically,
- the format is either a collection of one or more
- dictionaries passed to \*multiparams::
- conn.execute(
- table.insert(),
- {"id":1, "value":"v1"},
- {"id":2, "value":"v2"}
- )
- ...or individual key/values interpreted by \**params::
- conn.execute(
- table.insert(), id=1, value="v1"
- )
- In the case that a plain SQL string is passed, and the underlying
- DBAPI accepts positional bind parameters, a collection of tuples
- or individual values in \*multiparams may be passed::
- conn.execute(
- "INSERT INTO table (id, value) VALUES (?, ?)",
- (1, "v1"), (2, "v2")
- )
- conn.execute(
- "INSERT INTO table (id, value) VALUES (?, ?)",
- 1, "v1"
- )
- Note above, the usage of a question mark "?" or other
- symbol is contingent upon the "paramstyle" accepted by the DBAPI
- in use, which may be any of "qmark", "named", "pyformat", "format",
- "numeric". See `pep-249
- <https://www.python.org/dev/peps/pep-0249/>`_ for details on
- paramstyle.
- To execute a textual SQL statement which uses bound parameters in a
- DBAPI-agnostic way, use the :func:`_expression.text` construct.
- .. deprecated:: 2.0 use of tuple or scalar positional parameters
- is deprecated. All params should be dicts or sequences of dicts.
- Use :meth:`.exec_driver_sql` to execute a plain string with
- tuple or scalar positional parameters.
- """
- if isinstance(statement, util.string_types):
- util.warn_deprecated_20(
- "Passing a string to Connection.execute() is "
- "deprecated and will be removed in version 2.0. Use the "
- "text() construct, "
- "or the Connection.exec_driver_sql() method to invoke a "
- "driver-level SQL string."
- )
- return self._exec_driver_sql(
- statement,
- multiparams,
- params,
- _EMPTY_EXECUTION_OPTS,
- future=False,
- )
- try:
- meth = statement._execute_on_connection
- except AttributeError as err:
- util.raise_(
- exc.ObjectNotExecutableError(statement), replace_context=err
- )
- else:
- return meth(self, multiparams, params, _EMPTY_EXECUTION_OPTS)
- def _execute_function(self, func, multiparams, params, execution_options):
- """Execute a sql.FunctionElement object."""
- return self._execute_clauseelement(
- func.select(), multiparams, params, execution_options
- )
- def _execute_default(
- self,
- default,
- multiparams,
- params,
- # migrate is calling this directly :(
- execution_options=_EMPTY_EXECUTION_OPTS,
- ):
- """Execute a schema.ColumnDefault object."""
- execution_options = self._execution_options.merge_with(
- execution_options
- )
- distilled_parameters = _distill_params(self, multiparams, params)
- if self._has_events or self.engine._has_events:
- (
- default,
- distilled_params,
- event_multiparams,
- event_params,
- ) = self._invoke_before_exec_event(
- default, distilled_parameters, execution_options
- )
- try:
- conn = self._dbapi_connection
- if conn is None:
- conn = self._revalidate_connection()
- dialect = self.dialect
- ctx = dialect.execution_ctx_cls._init_default(
- dialect, self, conn, execution_options
- )
- except (exc.PendingRollbackError, exc.ResourceClosedError):
- raise
- except BaseException as e:
- self._handle_dbapi_exception(e, None, None, None, None)
- ret = ctx._exec_default(None, default, None)
- if self.should_close_with_result:
- self.close()
- if self._has_events or self.engine._has_events:
- self.dispatch.after_execute(
- self,
- default,
- event_multiparams,
- event_params,
- execution_options,
- ret,
- )
- return ret
- def _execute_ddl(self, ddl, multiparams, params, execution_options):
- """Execute a schema.DDL object."""
- execution_options = ddl._execution_options.merge_with(
- self._execution_options, execution_options
- )
- distilled_parameters = _distill_params(self, multiparams, params)
- if self._has_events or self.engine._has_events:
- (
- ddl,
- distilled_params,
- event_multiparams,
- event_params,
- ) = self._invoke_before_exec_event(
- ddl, distilled_parameters, execution_options
- )
- exec_opts = self._execution_options.merge_with(execution_options)
- schema_translate_map = exec_opts.get("schema_translate_map", None)
- dialect = self.dialect
- compiled = ddl.compile(
- dialect=dialect, schema_translate_map=schema_translate_map
- )
- ret = self._execute_context(
- dialect,
- dialect.execution_ctx_cls._init_ddl,
- compiled,
- None,
- execution_options,
- compiled,
- )
- if self._has_events or self.engine._has_events:
- self.dispatch.after_execute(
- self,
- ddl,
- event_multiparams,
- event_params,
- execution_options,
- ret,
- )
- return ret
- def _invoke_before_exec_event(
- self, elem, distilled_params, execution_options
- ):
- if len(distilled_params) == 1:
- event_multiparams, event_params = [], distilled_params[0]
- else:
- event_multiparams, event_params = distilled_params, {}
- for fn in self.dispatch.before_execute:
- elem, event_multiparams, event_params = fn(
- self,
- elem,
- event_multiparams,
- event_params,
- execution_options,
- )
- if event_multiparams:
- distilled_params = list(event_multiparams)
- if event_params:
- raise exc.InvalidRequestError(
- "Event handler can't return non-empty multiparams "
- "and params at the same time"
- )
- elif event_params:
- distilled_params = [event_params]
- else:
- distilled_params = []
- return elem, distilled_params, event_multiparams, event_params
- def _execute_clauseelement(
- self, elem, multiparams, params, execution_options
- ):
- """Execute a sql.ClauseElement object."""
- execution_options = elem._execution_options.merge_with(
- self._execution_options, execution_options
- )
- distilled_params = _distill_params(self, multiparams, params)
- has_events = self._has_events or self.engine._has_events
- if has_events:
- (
- elem,
- distilled_params,
- event_multiparams,
- event_params,
- ) = self._invoke_before_exec_event(
- elem, distilled_params, execution_options
- )
- if distilled_params:
- # ensure we don't retain a link to the view object for keys()
- # which links to the values, which we don't want to cache
- keys = sorted(distilled_params[0])
- for_executemany = len(distilled_params) > 1
- else:
- keys = []
- for_executemany = False
- dialect = self.dialect
- schema_translate_map = execution_options.get(
- "schema_translate_map", None
- )
- compiled_cache = execution_options.get(
- "compiled_cache", self.engine._compiled_cache
- )
- compiled_sql, extracted_params, cache_hit = elem._compile_w_cache(
- dialect=dialect,
- compiled_cache=compiled_cache,
- column_keys=keys,
- for_executemany=for_executemany,
- schema_translate_map=schema_translate_map,
- linting=self.dialect.compiler_linting | compiler.WARN_LINTING,
- )
- ret = self._execute_context(
- dialect,
- dialect.execution_ctx_cls._init_compiled,
- compiled_sql,
- distilled_params,
- execution_options,
- compiled_sql,
- distilled_params,
- elem,
- extracted_params,
- cache_hit=cache_hit,
- )
- if has_events:
- self.dispatch.after_execute(
- self,
- elem,
- event_multiparams,
- event_params,
- execution_options,
- ret,
- )
- return ret
- def _execute_compiled(
- self,
- compiled,
- multiparams,
- params,
- execution_options=_EMPTY_EXECUTION_OPTS,
- ):
- """Execute a sql.Compiled object.
- TODO: why do we have this? likely deprecate or remove
- """
- execution_options = compiled.execution_options.merge_with(
- self._execution_options, execution_options
- )
- distilled_parameters = _distill_params(self, multiparams, params)
- if self._has_events or self.engine._has_events:
- (
- compiled,
- distilled_params,
- event_multiparams,
- event_params,
- ) = self._invoke_before_exec_event(
- compiled, distilled_parameters, execution_options
- )
- dialect = self.dialect
- ret = self._execute_context(
- dialect,
- dialect.execution_ctx_cls._init_compiled,
- compiled,
- distilled_parameters,
- execution_options,
- compiled,
- distilled_parameters,
- None,
- None,
- )
- if self._has_events or self.engine._has_events:
- self.dispatch.after_execute(
- self,
- compiled,
- event_multiparams,
- event_params,
- execution_options,
- ret,
- )
- return ret
- def _exec_driver_sql(
- self, statement, multiparams, params, execution_options, future
- ):
- execution_options = self._execution_options.merge_with(
- execution_options
- )
- distilled_parameters = _distill_params(self, multiparams, params)
- if not future:
- if self._has_events or self.engine._has_events:
- (
- statement,
- distilled_params,
- event_multiparams,
- event_params,
- ) = self._invoke_before_exec_event(
- statement, distilled_parameters, execution_options
- )
- dialect = self.dialect
- ret = self._execute_context(
- dialect,
- dialect.execution_ctx_cls._init_statement,
- statement,
- distilled_parameters,
- execution_options,
- statement,
- distilled_parameters,
- )
- if not future:
- if self._has_events or self.engine._has_events:
- self.dispatch.after_execute(
- self,
- statement,
- event_multiparams,
- event_params,
- execution_options,
- ret,
- )
- return ret
- def _execute_20(
- self,
- statement,
- parameters=None,
- execution_options=_EMPTY_EXECUTION_OPTS,
- ):
- args_10style, kwargs_10style = _distill_params_20(parameters)
- try:
- meth = statement._execute_on_connection
- except AttributeError as err:
- util.raise_(
- exc.ObjectNotExecutableError(statement), replace_context=err
- )
- else:
- return meth(self, args_10style, kwargs_10style, execution_options)
- def exec_driver_sql(
- self, statement, parameters=None, execution_options=None
- ):
- r"""Executes a SQL statement construct and returns a
- :class:`_engine.CursorResult`.
- :param statement: The statement str to be executed. Bound parameters
- must use the underlying DBAPI's paramstyle, such as "qmark",
- "pyformat", "format", etc.
- :param parameters: represent bound parameter values to be used in the
- execution. The format is one of: a dictionary of named parameters,
- a tuple of positional parameters, or a list containing either
- dictionaries or tuples for multiple-execute support.
- E.g. multiple dictionaries::
- conn.exec_driver_sql(
- "INSERT INTO table (id, value) VALUES (%(id)s, %(value)s)",
- [{"id":1, "value":"v1"}, {"id":2, "value":"v2"}]
- )
- Single dictionary::
- conn.exec_driver_sql(
- "INSERT INTO table (id, value) VALUES (%(id)s, %(value)s)",
- dict(id=1, value="v1")
- )
- Single tuple::
- conn.exec_driver_sql(
- "INSERT INTO table (id, value) VALUES (?, ?)",
- (1, 'v1')
- )
- .. note:: The :meth:`_engine.Connection.exec_driver_sql` method does
- not participate in the
- :meth:`_events.ConnectionEvents.before_execute` and
- :meth:`_events.ConnectionEvents.after_execute` events. To
- intercept calls to :meth:`_engine.Connection.exec_driver_sql`, use
- :meth:`_events.ConnectionEvents.before_cursor_execute` and
- :meth:`_events.ConnectionEvents.after_cursor_execute`.
- .. seealso::
- :pep:`249`
- """
- args_10style, kwargs_10style = _distill_params_20(parameters)
- return self._exec_driver_sql(
- statement,
- args_10style,
- kwargs_10style,
- execution_options,
- future=True,
- )
- def _execute_context(
- self,
- dialect,
- constructor,
- statement,
- parameters,
- execution_options,
- *args,
- **kw
- ):
- """Create an :class:`.ExecutionContext` and execute, returning
- a :class:`_engine.CursorResult`."""
- branched = self
- if self.__branch_from:
- # if this is a "branched" connection, do everything in terms
- # of the "root" connection, *except* for .close(), which is
- # the only feature that branching provides
- self = self.__branch_from
- try:
- conn = self._dbapi_connection
- if conn is None:
- conn = self._revalidate_connection()
- context = constructor(
- dialect, self, conn, execution_options, *args, **kw
- )
- except (exc.PendingRollbackError, exc.ResourceClosedError):
- raise
- except BaseException as e:
- self._handle_dbapi_exception(
- e, util.text_type(statement), parameters, None, None
- )
- if (
- self._transaction
- and not self._transaction.is_active
- or (
- self._nested_transaction
- and not self._nested_transaction.is_active
- )
- ):
- self._invalid_transaction()
- elif self._trans_context_manager:
- TransactionalContext._trans_ctx_check(self)
- if self._is_future and self._transaction is None:
- self._autobegin()
- context.pre_exec()
- if dialect.use_setinputsizes:
- context._set_input_sizes()
- cursor, statement, parameters = (
- context.cursor,
- context.statement,
- context.parameters,
- )
- if not context.executemany:
- parameters = parameters[0]
- if self._has_events or self.engine._has_events:
- for fn in self.dispatch.before_cursor_execute:
- statement, parameters = fn(
- self,
- cursor,
- statement,
- parameters,
- context,
- context.executemany,
- )
- if self._echo:
- self._log_info(statement)
- stats = context._get_cache_stats()
- if not self.engine.hide_parameters:
- self._log_info(
- "[%s] %r",
- stats,
- sql_util._repr_params(
- parameters, batches=10, ismulti=context.executemany
- ),
- )
- else:
- self._log_info(
- "[%s] [SQL parameters hidden due to hide_parameters=True]"
- % (stats,)
- )
- evt_handled = False
- try:
- if context.executemany:
- if self.dialect._has_events:
- for fn in self.dialect.dispatch.do_executemany:
- if fn(cursor, statement, parameters, context):
- evt_handled = True
- break
- if not evt_handled:
- self.dialect.do_executemany(
- cursor, statement, parameters, context
- )
- elif not parameters and context.no_parameters:
- if self.dialect._has_events:
- for fn in self.dialect.dispatch.do_execute_no_params:
- if fn(cursor, statement, context):
- evt_handled = True
- break
- if not evt_handled:
- self.dialect.do_execute_no_params(
- cursor, statement, context
- )
- else:
- if self.dialect._has_events:
- for fn in self.dialect.dispatch.do_execute:
- if fn(cursor, statement, parameters, context):
- evt_handled = True
- break
- if not evt_handled:
- self.dialect.do_execute(
- cursor, statement, parameters, context
- )
- if self._has_events or self.engine._has_events:
- self.dispatch.after_cursor_execute(
- self,
- cursor,
- statement,
- parameters,
- context,
- context.executemany,
- )
- context.post_exec()
- result = context._setup_result_proxy()
- if not self._is_future:
- should_close_with_result = branched.should_close_with_result
- if not result._soft_closed and should_close_with_result:
- result._autoclose_connection = True
- if (
- # usually we're in a transaction so avoid relatively
- # expensive / legacy should_autocommit call
- self._transaction is None
- and context.should_autocommit
- ):
- self._commit_impl(autocommit=True)
- # for "connectionless" execution, we have to close this
- # Connection after the statement is complete.
- # legacy stuff.
- if should_close_with_result and context._soft_closed:
- assert not self._is_future
- # CursorResult already exhausted rows / has no rows.
- # close us now
- branched.close()
- except BaseException as e:
- self._handle_dbapi_exception(
- e, statement, parameters, cursor, context
- )
- return result
- def _cursor_execute(self, cursor, statement, parameters, context=None):
- """Execute a statement + params on the given cursor.
- Adds appropriate logging and exception handling.
- This method is used by DefaultDialect for special-case
- executions, such as for sequences and column defaults.
- The path of statement execution in the majority of cases
- terminates at _execute_context().
- """
- if self._has_events or self.engine._has_events:
- for fn in self.dispatch.before_cursor_execute:
- statement, parameters = fn(
- self, cursor, statement, parameters, context, False
- )
- if self._echo:
- self._log_info(statement)
- self._log_info("[raw sql] %r", parameters)
- try:
- for fn in (
- ()
- if not self.dialect._has_events
- else self.dialect.dispatch.do_execute
- ):
- if fn(cursor, statement, parameters, context):
- break
- else:
- self.dialect.do_execute(cursor, statement, parameters, context)
- except BaseException as e:
- self._handle_dbapi_exception(
- e, statement, parameters, cursor, context
- )
- if self._has_events or self.engine._has_events:
- self.dispatch.after_cursor_execute(
- self, cursor, statement, parameters, context, False
- )
- def _safe_close_cursor(self, cursor):
- """Close the given cursor, catching exceptions
- and turning into log warnings.
- """
- try:
- cursor.close()
- except Exception:
- # log the error through the connection pool's logger.
- self.engine.pool.logger.error(
- "Error closing cursor", exc_info=True
- )
- _reentrant_error = False
- _is_disconnect = False
- def _handle_dbapi_exception(
- self, e, statement, parameters, cursor, context
- ):
- exc_info = sys.exc_info()
- is_exit_exception = util.is_exit_exception(e)
- if not self._is_disconnect:
- self._is_disconnect = (
- isinstance(e, self.dialect.dbapi.Error)
- and not self.closed
- and self.dialect.is_disconnect(
- e,
- self._dbapi_connection if not self.invalidated else None,
- cursor,
- )
- ) or (is_exit_exception and not self.closed)
- invalidate_pool_on_disconnect = not is_exit_exception
- if self._reentrant_error:
- util.raise_(
- exc.DBAPIError.instance(
- statement,
- parameters,
- e,
- self.dialect.dbapi.Error,
- hide_parameters=self.engine.hide_parameters,
- dialect=self.dialect,
- ismulti=context.executemany
- if context is not None
- else None,
- ),
- with_traceback=exc_info[2],
- from_=e,
- )
- self._reentrant_error = True
- try:
- # non-DBAPI error - if we already got a context,
- # or there's no string statement, don't wrap it
- should_wrap = isinstance(e, self.dialect.dbapi.Error) or (
- statement is not None
- and context is None
- and not is_exit_exception
- )
- if should_wrap:
- sqlalchemy_exception = exc.DBAPIError.instance(
- statement,
- parameters,
- e,
- self.dialect.dbapi.Error,
- hide_parameters=self.engine.hide_parameters,
- connection_invalidated=self._is_disconnect,
- dialect=self.dialect,
- ismulti=context.executemany
- if context is not None
- else None,
- )
- else:
- sqlalchemy_exception = None
- newraise = None
- if (
- self._has_events or self.engine._has_events
- ) and not self._execution_options.get(
- "skip_user_error_events", False
- ):
- ctx = ExceptionContextImpl(
- e,
- sqlalchemy_exception,
- self.engine,
- self,
- cursor,
- statement,
- parameters,
- context,
- self._is_disconnect,
- invalidate_pool_on_disconnect,
- )
- for fn in self.dispatch.handle_error:
- try:
- # handler returns an exception;
- # call next handler in a chain
- per_fn = fn(ctx)
- if per_fn is not None:
- ctx.chained_exception = newraise = per_fn
- except Exception as _raised:
- # handler raises an exception - stop processing
- newraise = _raised
- break
- if self._is_disconnect != ctx.is_disconnect:
- self._is_disconnect = ctx.is_disconnect
- if sqlalchemy_exception:
- sqlalchemy_exception.connection_invalidated = (
- ctx.is_disconnect
- )
- # set up potentially user-defined value for
- # invalidate pool.
- invalidate_pool_on_disconnect = (
- ctx.invalidate_pool_on_disconnect
- )
- if should_wrap and context:
- context.handle_dbapi_exception(e)
- if not self._is_disconnect:
- if cursor:
- self._safe_close_cursor(cursor)
- with util.safe_reraise(warn_only=True):
- self._autorollback()
- if newraise:
- util.raise_(newraise, with_traceback=exc_info[2], from_=e)
- elif should_wrap:
- util.raise_(
- sqlalchemy_exception, with_traceback=exc_info[2], from_=e
- )
- else:
- util.raise_(exc_info[1], with_traceback=exc_info[2])
- finally:
- del self._reentrant_error
- if self._is_disconnect:
- del self._is_disconnect
- if not self.invalidated:
- dbapi_conn_wrapper = self._dbapi_connection
- if invalidate_pool_on_disconnect:
- self.engine.pool._invalidate(dbapi_conn_wrapper, e)
- self.invalidate(e)
- if self.should_close_with_result:
- assert not self._is_future
- self.close()
- @classmethod
- def _handle_dbapi_exception_noconnection(cls, e, dialect, engine):
- exc_info = sys.exc_info()
- is_disconnect = dialect.is_disconnect(e, None, None)
- should_wrap = isinstance(e, dialect.dbapi.Error)
- if should_wrap:
- sqlalchemy_exception = exc.DBAPIError.instance(
- None,
- None,
- e,
- dialect.dbapi.Error,
- hide_parameters=engine.hide_parameters,
- connection_invalidated=is_disconnect,
- )
- else:
- sqlalchemy_exception = None
- newraise = None
- if engine._has_events:
- ctx = ExceptionContextImpl(
- e,
- sqlalchemy_exception,
- engine,
- None,
- None,
- None,
- None,
- None,
- is_disconnect,
- True,
- )
- for fn in engine.dispatch.handle_error:
- try:
- # handler returns an exception;
- # call next handler in a chain
- per_fn = fn(ctx)
- if per_fn is not None:
- ctx.chained_exception = newraise = per_fn
- except Exception as _raised:
- # handler raises an exception - stop processing
- newraise = _raised
- break
- if sqlalchemy_exception and is_disconnect != ctx.is_disconnect:
- sqlalchemy_exception.connection_invalidated = (
- is_disconnect
- ) = ctx.is_disconnect
- if newraise:
- util.raise_(newraise, with_traceback=exc_info[2], from_=e)
- elif should_wrap:
- util.raise_(
- sqlalchemy_exception, with_traceback=exc_info[2], from_=e
- )
- else:
- util.raise_(exc_info[1], with_traceback=exc_info[2])
- def _run_ddl_visitor(self, visitorcallable, element, **kwargs):
- """run a DDL visitor.
- This method is only here so that the MockConnection can change the
- options given to the visitor so that "checkfirst" is skipped.
- """
- visitorcallable(self.dialect, self, **kwargs).traverse_single(element)
- @util.deprecated(
- "1.4",
- "The :meth:`_engine.Connection.transaction` "
- "method is deprecated and will be "
- "removed in a future release. Use the :meth:`_engine.Engine.begin` "
- "context manager instead.",
- )
- def transaction(self, callable_, *args, **kwargs):
- r"""Execute the given function within a transaction boundary.
- The function is passed this :class:`_engine.Connection`
- as the first argument, followed by the given \*args and \**kwargs,
- e.g.::
- def do_something(conn, x, y):
- conn.execute(text("some statement"), {'x':x, 'y':y})
- conn.transaction(do_something, 5, 10)
- The operations inside the function are all invoked within the
- context of a single :class:`.Transaction`.
- Upon success, the transaction is committed. If an
- exception is raised, the transaction is rolled back
- before propagating the exception.
- .. note::
- The :meth:`.transaction` method is superseded by
- the usage of the Python ``with:`` statement, which can
- be used with :meth:`_engine.Connection.begin`::
- with conn.begin():
- conn.execute(text("some statement"), {'x':5, 'y':10})
- As well as with :meth:`_engine.Engine.begin`::
- with engine.begin() as conn:
- conn.execute(text("some statement"), {'x':5, 'y':10})
- .. seealso::
- :meth:`_engine.Engine.begin` - engine-level transactional
- context
- :meth:`_engine.Engine.transaction` - engine-level version of
- :meth:`_engine.Connection.transaction`
- """
- kwargs["_sa_skip_warning"] = True
- trans = self.begin()
- try:
- ret = self.run_callable(callable_, *args, **kwargs)
- trans.commit()
- return ret
- except:
- with util.safe_reraise():
- trans.rollback()
- @util.deprecated(
- "1.4",
- "The :meth:`_engine.Connection.run_callable` "
- "method is deprecated and will "
- "be removed in a future release. Invoke the callable function "
- "directly, passing the Connection.",
- )
- def run_callable(self, callable_, *args, **kwargs):
- r"""Given a callable object or function, execute it, passing
- a :class:`_engine.Connection` as the first argument.
- The given \*args and \**kwargs are passed subsequent
- to the :class:`_engine.Connection` argument.
- This function, along with :meth:`_engine.Engine.run_callable`,
- allows a function to be run with a :class:`_engine.Connection`
- or :class:`_engine.Engine` object without the need to know
- which one is being dealt with.
- """
- return callable_(self, *args, **kwargs)
- class ExceptionContextImpl(ExceptionContext):
- """Implement the :class:`.ExceptionContext` interface."""
- def __init__(
- self,
- exception,
- sqlalchemy_exception,
- engine,
- connection,
- cursor,
- statement,
- parameters,
- context,
- is_disconnect,
- invalidate_pool_on_disconnect,
- ):
- self.engine = engine
- self.connection = connection
- self.sqlalchemy_exception = sqlalchemy_exception
- self.original_exception = exception
- self.execution_context = context
- self.statement = statement
- self.parameters = parameters
- self.is_disconnect = is_disconnect
- self.invalidate_pool_on_disconnect = invalidate_pool_on_disconnect
- class Transaction(TransactionalContext):
- """Represent a database transaction in progress.
- The :class:`.Transaction` object is procured by
- calling the :meth:`_engine.Connection.begin` method of
- :class:`_engine.Connection`::
- from sqlalchemy import create_engine
- engine = create_engine("postgresql://scott:tiger@localhost/test")
- connection = engine.connect()
- trans = connection.begin()
- connection.execute(text("insert into x (a, b) values (1, 2)"))
- trans.commit()
- The object provides :meth:`.rollback` and :meth:`.commit`
- methods in order to control transaction boundaries. It
- also implements a context manager interface so that
- the Python ``with`` statement can be used with the
- :meth:`_engine.Connection.begin` method::
- with connection.begin():
- connection.execute(text("insert into x (a, b) values (1, 2)"))
- The Transaction object is **not** threadsafe.
- .. seealso::
- :meth:`_engine.Connection.begin`
- :meth:`_engine.Connection.begin_twophase`
- :meth:`_engine.Connection.begin_nested`
- .. index::
- single: thread safety; Transaction
- """
- __slots__ = ()
- _is_root = False
- def __init__(self, connection):
- raise NotImplementedError()
- def _do_deactivate(self):
- """do whatever steps are necessary to set this transaction as
- "deactive", however leave this transaction object in place as far
- as the connection's state.
- for a "real" transaction this should roll back the transaction
- and ensure this transaction is no longer a reset agent.
- this is used for nesting of marker transactions where the marker
- can set the "real" transaction as rolled back, however it stays
- in place.
- for 2.0 we hope to remove this nesting feature.
- """
- raise NotImplementedError()
- @property
- def _deactivated_from_connection(self):
- """True if this transaction is totally deactivated from the connection
- and therefore can no longer affect its state.
- """
- raise NotImplementedError()
- def _do_close(self):
- raise NotImplementedError()
- def _do_rollback(self):
- raise NotImplementedError()
- def _do_commit(self):
- raise NotImplementedError()
- @property
- def is_valid(self):
- return self.is_active and not self.connection.invalidated
- def close(self):
- """Close this :class:`.Transaction`.
- If this transaction is the base transaction in a begin/commit
- nesting, the transaction will rollback(). Otherwise, the
- method returns.
- This is used to cancel a Transaction without affecting the scope of
- an enclosing transaction.
- """
- try:
- self._do_close()
- finally:
- assert not self.is_active
- def rollback(self):
- """Roll back this :class:`.Transaction`.
- The implementation of this may vary based on the type of transaction in
- use:
- * For a simple database transaction (e.g. :class:`.RootTransaction`),
- it corresponds to a ROLLBACK.
- * For a :class:`.NestedTransaction`, it corresponds to a
- "ROLLBACK TO SAVEPOINT" operation.
- * For a :class:`.TwoPhaseTransaction`, DBAPI-specific methods for two
- phase transactions may be used.
- """
- try:
- self._do_rollback()
- finally:
- assert not self.is_active
- def commit(self):
- """Commit this :class:`.Transaction`.
- The implementation of this may vary based on the type of transaction in
- use:
- * For a simple database transaction (e.g. :class:`.RootTransaction`),
- it corresponds to a COMMIT.
- * For a :class:`.NestedTransaction`, it corresponds to a
- "RELEASE SAVEPOINT" operation.
- * For a :class:`.TwoPhaseTransaction`, DBAPI-specific methods for two
- phase transactions may be used.
- """
- try:
- self._do_commit()
- finally:
- assert not self.is_active
- def _get_subject(self):
- return self.connection
- def _transaction_is_active(self):
- return self.is_active
- def _transaction_is_closed(self):
- return not self._deactivated_from_connection
- def _rollback_can_be_called(self):
- # for RootTransaction / NestedTransaction, it's safe to call
- # rollback() even if the transaction is deactive and no warnings
- # will be emitted. tested in
- # test_transaction.py -> test_no_rollback_in_deactive(?:_savepoint)?
- return True
- class MarkerTransaction(Transaction):
- """A 'marker' transaction that is used for nested begin() calls.
- .. deprecated:: 1.4 future connection for 2.0 won't support this pattern.
- """
- __slots__ = ("connection", "_is_active", "_transaction")
- def __init__(self, connection):
- assert connection._transaction is not None
- if not connection._transaction.is_active:
- raise exc.InvalidRequestError(
- "the current transaction on this connection is inactive. "
- "Please issue a rollback first."
- )
- assert not connection._is_future
- util.warn_deprecated_20(
- "Calling .begin() when a transaction is already begun, creating "
- "a 'sub' transaction, is deprecated "
- "and will be removed in 2.0. See the documentation section "
- "'Migrating from the nesting pattern' for background on how "
- "to migrate from this pattern."
- )
- self.connection = connection
- if connection._trans_context_manager:
- TransactionalContext._trans_ctx_check(connection)
- if connection._nested_transaction is not None:
- self._transaction = connection._nested_transaction
- else:
- self._transaction = connection._transaction
- self._is_active = True
- @property
- def _deactivated_from_connection(self):
- return not self.is_active
- @property
- def is_active(self):
- return self._is_active and self._transaction.is_active
- def _deactivate(self):
- self._is_active = False
- def _do_close(self):
- # does not actually roll back the root
- self._deactivate()
- def _do_rollback(self):
- # does roll back the root
- if self._is_active:
- try:
- self._transaction._do_deactivate()
- finally:
- self._deactivate()
- def _do_commit(self):
- self._deactivate()
- class RootTransaction(Transaction):
- """Represent the "root" transaction on a :class:`_engine.Connection`.
- This corresponds to the current "BEGIN/COMMIT/ROLLBACK" that's occurring
- for the :class:`_engine.Connection`. The :class:`_engine.RootTransaction`
- is created by calling upon the :meth:`_engine.Connection.begin` method, and
- remains associated with the :class:`_engine.Connection` throughout its
- active span. The current :class:`_engine.RootTransaction` in use is
- accessible via the :attr:`_engine.Connection.get_transaction` method of
- :class:`_engine.Connection`.
- In :term:`2.0 style` use, the :class:`_future.Connection` also employs
- "autobegin" behavior that will create a new
- :class:`_engine.RootTransaction` whenever a connection in a
- non-transactional state is used to emit commands on the DBAPI connection.
- The scope of the :class:`_engine.RootTransaction` in 2.0 style
- use can be controlled using the :meth:`_future.Connection.commit` and
- :meth:`_future.Connection.rollback` methods.
- """
- _is_root = True
- __slots__ = ("connection", "is_active")
- def __init__(self, connection):
- assert connection._transaction is None
- if connection._trans_context_manager:
- TransactionalContext._trans_ctx_check(connection)
- self.connection = connection
- self._connection_begin_impl()
- connection._transaction = self
- self.is_active = True
- def _deactivate_from_connection(self):
- if self.is_active:
- assert self.connection._transaction is self
- self.is_active = False
- elif self.connection._transaction is not self:
- util.warn("transaction already deassociated from connection")
- @property
- def _deactivated_from_connection(self):
- return self.connection._transaction is not self
- def _do_deactivate(self):
- # called from a MarkerTransaction to cancel this root transaction.
- # the transaction stays in place as connection._transaction, but
- # is no longer active and is no longer the reset agent for the
- # pooled connection. the connection won't support a new begin()
- # until this transaction is explicitly closed, rolled back,
- # or committed.
- assert self.connection._transaction is self
- if self.is_active:
- self._connection_rollback_impl()
- # handle case where a savepoint was created inside of a marker
- # transaction that refers to a root. nested has to be cancelled
- # also.
- if self.connection._nested_transaction:
- self.connection._nested_transaction._cancel()
- self._deactivate_from_connection()
- def _connection_begin_impl(self):
- self.connection._begin_impl(self)
- def _connection_rollback_impl(self):
- self.connection._rollback_impl()
- def _connection_commit_impl(self):
- self.connection._commit_impl()
- def _close_impl(self, try_deactivate=False):
- try:
- if self.is_active:
- self._connection_rollback_impl()
- if self.connection._nested_transaction:
- self.connection._nested_transaction._cancel()
- finally:
- if self.is_active or try_deactivate:
- self._deactivate_from_connection()
- if self.connection._transaction is self:
- self.connection._transaction = None
- assert not self.is_active
- assert self.connection._transaction is not self
- def _do_close(self):
- self._close_impl()
- def _do_rollback(self):
- self._close_impl(try_deactivate=True)
- def _do_commit(self):
- if self.is_active:
- assert self.connection._transaction is self
- try:
- self._connection_commit_impl()
- finally:
- # whether or not commit succeeds, cancel any
- # nested transactions, make this transaction "inactive"
- # and remove it as a reset agent
- if self.connection._nested_transaction:
- self.connection._nested_transaction._cancel()
- self._deactivate_from_connection()
- # ...however only remove as the connection's current transaction
- # if commit succeeded. otherwise it stays on so that a rollback
- # needs to occur.
- self.connection._transaction = None
- else:
- if self.connection._transaction is self:
- self.connection._invalid_transaction()
- else:
- raise exc.InvalidRequestError("This transaction is inactive")
- assert not self.is_active
- assert self.connection._transaction is not self
- class NestedTransaction(Transaction):
- """Represent a 'nested', or SAVEPOINT transaction.
- The :class:`.NestedTransaction` object is created by calling the
- :meth:`_engine.Connection.begin_nested` method of
- :class:`_engine.Connection`.
- When using :class:`.NestedTransaction`, the semantics of "begin" /
- "commit" / "rollback" are as follows:
- * the "begin" operation corresponds to the "BEGIN SAVEPOINT" command, where
- the savepoint is given an explicit name that is part of the state
- of this object.
- * The :meth:`.NestedTransaction.commit` method corresponds to a
- "RELEASE SAVEPOINT" operation, using the savepoint identifier associated
- with this :class:`.NestedTransaction`.
- * The :meth:`.NestedTransaction.rollback` method corresponds to a
- "ROLLBACK TO SAVEPOINT" operation, using the savepoint identifier
- associated with this :class:`.NestedTransaction`.
- The rationale for mimicking the semantics of an outer transaction in
- terms of savepoints so that code may deal with a "savepoint" transaction
- and an "outer" transaction in an agnostic way.
- .. seealso::
- :ref:`session_begin_nested` - ORM version of the SAVEPOINT API.
- """
- __slots__ = ("connection", "is_active", "_savepoint", "_previous_nested")
- def __init__(self, connection):
- assert connection._transaction is not None
- if connection._trans_context_manager:
- TransactionalContext._trans_ctx_check(connection)
- self.connection = connection
- self._savepoint = self.connection._savepoint_impl()
- self.is_active = True
- self._previous_nested = connection._nested_transaction
- connection._nested_transaction = self
- def _deactivate_from_connection(self, warn=True):
- if self.connection._nested_transaction is self:
- self.connection._nested_transaction = self._previous_nested
- elif warn:
- util.warn(
- "nested transaction already deassociated from connection"
- )
- @property
- def _deactivated_from_connection(self):
- return self.connection._nested_transaction is not self
- def _cancel(self):
- # called by RootTransaction when the outer transaction is
- # committed, rolled back, or closed to cancel all savepoints
- # without any action being taken
- self.is_active = False
- self._deactivate_from_connection()
- if self._previous_nested:
- self._previous_nested._cancel()
- def _close_impl(self, deactivate_from_connection, warn_already_deactive):
- try:
- if self.is_active and self.connection._transaction.is_active:
- self.connection._rollback_to_savepoint_impl(self._savepoint)
- finally:
- self.is_active = False
- if deactivate_from_connection:
- self._deactivate_from_connection(warn=warn_already_deactive)
- assert not self.is_active
- if deactivate_from_connection:
- assert self.connection._nested_transaction is not self
- def _do_deactivate(self):
- self._close_impl(False, False)
- def _do_close(self):
- self._close_impl(True, False)
- def _do_rollback(self):
- self._close_impl(True, True)
- def _do_commit(self):
- if self.is_active:
- try:
- self.connection._release_savepoint_impl(self._savepoint)
- finally:
- # nested trans becomes inactive on failed release
- # unconditionally. this prevents it from trying to
- # emit SQL when it rolls back.
- self.is_active = False
- # but only de-associate from connection if it succeeded
- self._deactivate_from_connection()
- else:
- if self.connection._nested_transaction is self:
- self.connection._invalid_transaction()
- else:
- raise exc.InvalidRequestError(
- "This nested transaction is inactive"
- )
- class TwoPhaseTransaction(RootTransaction):
- """Represent a two-phase transaction.
- A new :class:`.TwoPhaseTransaction` object may be procured
- using the :meth:`_engine.Connection.begin_twophase` method.
- The interface is the same as that of :class:`.Transaction`
- with the addition of the :meth:`prepare` method.
- """
- __slots__ = ("connection", "is_active", "xid", "_is_prepared")
- def __init__(self, connection, xid):
- self._is_prepared = False
- self.xid = xid
- super(TwoPhaseTransaction, self).__init__(connection)
- def prepare(self):
- """Prepare this :class:`.TwoPhaseTransaction`.
- After a PREPARE, the transaction can be committed.
- """
- if not self.is_active:
- raise exc.InvalidRequestError("This transaction is inactive")
- self.connection._prepare_twophase_impl(self.xid)
- self._is_prepared = True
- def _connection_begin_impl(self):
- self.connection._begin_twophase_impl(self)
- def _connection_rollback_impl(self):
- self.connection._rollback_twophase_impl(self.xid, self._is_prepared)
- def _connection_commit_impl(self):
- self.connection._commit_twophase_impl(self.xid, self._is_prepared)
- class Engine(Connectable, log.Identified):
- """
- Connects a :class:`~sqlalchemy.pool.Pool` and
- :class:`~sqlalchemy.engine.interfaces.Dialect` together to provide a
- source of database connectivity and behavior.
- This is the **SQLAlchemy 1.x version** of :class:`_engine.Engine`. For
- the :term:`2.0 style` version, which includes some API differences,
- see :class:`_future.Engine`.
- An :class:`_engine.Engine` object is instantiated publicly using the
- :func:`~sqlalchemy.create_engine` function.
- .. seealso::
- :doc:`/core/engines`
- :ref:`connections_toplevel`
- """
- _execution_options = _EMPTY_EXECUTION_OPTS
- _has_events = False
- _connection_cls = Connection
- _sqla_logger_namespace = "sqlalchemy.engine.Engine"
- _is_future = False
- _schema_translate_map = None
- def __init__(
- self,
- pool,
- dialect,
- url,
- logging_name=None,
- echo=None,
- query_cache_size=500,
- execution_options=None,
- hide_parameters=False,
- ):
- self.pool = pool
- self.url = url
- self.dialect = dialect
- if logging_name:
- self.logging_name = logging_name
- self.echo = echo
- self.hide_parameters = hide_parameters
- if query_cache_size != 0:
- self._compiled_cache = util.LRUCache(
- query_cache_size, size_alert=self._lru_size_alert
- )
- else:
- self._compiled_cache = None
- log.instance_logger(self, echoflag=echo)
- if execution_options:
- self.update_execution_options(**execution_options)
- def _lru_size_alert(self, cache):
- if self._should_log_info:
- self.logger.info(
- "Compiled cache size pruning from %d items to %d. "
- "Increase cache size to reduce the frequency of pruning.",
- len(cache),
- cache.capacity,
- )
- @property
- def engine(self):
- return self
- def clear_compiled_cache(self):
- """Clear the compiled cache associated with the dialect.
- This applies **only** to the built-in cache that is established
- via the :paramref:`_engine.create_engine.query_cache_size` parameter.
- It will not impact any dictionary caches that were passed via the
- :paramref:`.Connection.execution_options.query_cache` parameter.
- .. versionadded:: 1.4
- """
- if self._compiled_cache:
- self._compiled_cache.clear()
- def update_execution_options(self, **opt):
- r"""Update the default execution_options dictionary
- of this :class:`_engine.Engine`.
- The given keys/values in \**opt are added to the
- default execution options that will be used for
- all connections. The initial contents of this dictionary
- can be sent via the ``execution_options`` parameter
- to :func:`_sa.create_engine`.
- .. seealso::
- :meth:`_engine.Connection.execution_options`
- :meth:`_engine.Engine.execution_options`
- """
- self._execution_options = self._execution_options.union(opt)
- self.dispatch.set_engine_execution_options(self, opt)
- self.dialect.set_engine_execution_options(self, opt)
- def execution_options(self, **opt):
- """Return a new :class:`_engine.Engine` that will provide
- :class:`_engine.Connection` objects with the given execution options.
- The returned :class:`_engine.Engine` remains related to the original
- :class:`_engine.Engine` in that it shares the same connection pool and
- other state:
- * The :class:`_pool.Pool` used by the new :class:`_engine.Engine`
- is the
- same instance. The :meth:`_engine.Engine.dispose`
- method will replace
- the connection pool instance for the parent engine as well
- as this one.
- * Event listeners are "cascaded" - meaning, the new
- :class:`_engine.Engine`
- inherits the events of the parent, and new events can be associated
- with the new :class:`_engine.Engine` individually.
- * The logging configuration and logging_name is copied from the parent
- :class:`_engine.Engine`.
- The intent of the :meth:`_engine.Engine.execution_options` method is
- to implement "sharding" schemes where multiple :class:`_engine.Engine`
- objects refer to the same connection pool, but are differentiated
- by options that would be consumed by a custom event::
- primary_engine = create_engine("mysql://")
- shard1 = primary_engine.execution_options(shard_id="shard1")
- shard2 = primary_engine.execution_options(shard_id="shard2")
- Above, the ``shard1`` engine serves as a factory for
- :class:`_engine.Connection`
- objects that will contain the execution option
- ``shard_id=shard1``, and ``shard2`` will produce
- :class:`_engine.Connection`
- objects that contain the execution option ``shard_id=shard2``.
- An event handler can consume the above execution option to perform
- a schema switch or other operation, given a connection. Below
- we emit a MySQL ``use`` statement to switch databases, at the same
- time keeping track of which database we've established using the
- :attr:`_engine.Connection.info` dictionary,
- which gives us a persistent
- storage space that follows the DBAPI connection::
- from sqlalchemy import event
- from sqlalchemy.engine import Engine
- shards = {"default": "base", shard_1: "db1", "shard_2": "db2"}
- @event.listens_for(Engine, "before_cursor_execute")
- def _switch_shard(conn, cursor, stmt,
- params, context, executemany):
- shard_id = conn._execution_options.get('shard_id', "default")
- current_shard = conn.info.get("current_shard", None)
- if current_shard != shard_id:
- cursor.execute("use %s" % shards[shard_id])
- conn.info["current_shard"] = shard_id
- .. seealso::
- :meth:`_engine.Connection.execution_options`
- - update execution options
- on a :class:`_engine.Connection` object.
- :meth:`_engine.Engine.update_execution_options`
- - update the execution
- options for a given :class:`_engine.Engine` in place.
- :meth:`_engine.Engine.get_execution_options`
- """
- return self._option_cls(self, opt)
- def get_execution_options(self):
- """Get the non-SQL options which will take effect during execution.
- .. versionadded: 1.3
- .. seealso::
- :meth:`_engine.Engine.execution_options`
- """
- return self._execution_options
- @property
- def name(self):
- """String name of the :class:`~sqlalchemy.engine.interfaces.Dialect`
- in use by this :class:`Engine`."""
- return self.dialect.name
- @property
- def driver(self):
- """Driver name of the :class:`~sqlalchemy.engine.interfaces.Dialect`
- in use by this :class:`Engine`."""
- return self.dialect.driver
- echo = log.echo_property()
- def __repr__(self):
- return "Engine(%r)" % (self.url,)
- def dispose(self):
- """Dispose of the connection pool used by this
- :class:`_engine.Engine`.
- This has the effect of fully closing all **currently checked in**
- database connections. Connections that are still checked out
- will **not** be closed, however they will no longer be associated
- with this :class:`_engine.Engine`,
- so when they are closed individually,
- eventually the :class:`_pool.Pool` which they are associated with will
- be garbage collected and they will be closed out fully, if
- not already closed on checkin.
- A new connection pool is created immediately after the old one has
- been disposed. This new pool, like all SQLAlchemy connection pools,
- does not make any actual connections to the database until one is
- first requested, so as long as the :class:`_engine.Engine`
- isn't used again,
- no new connections will be made.
- .. seealso::
- :ref:`engine_disposal`
- """
- self.pool.dispose()
- self.pool = self.pool.recreate()
- self.dispatch.engine_disposed(self)
- def _execute_default(
- self, default, multiparams=(), params=util.EMPTY_DICT
- ):
- with self.connect() as conn:
- return conn._execute_default(default, multiparams, params)
- @contextlib.contextmanager
- def _optional_conn_ctx_manager(self, connection=None):
- if connection is None:
- with self.connect() as conn:
- yield conn
- else:
- yield connection
- class _trans_ctx(object):
- def __init__(self, conn, transaction, close_with_result):
- self.conn = conn
- self.transaction = transaction
- self.close_with_result = close_with_result
- def __enter__(self):
- self.transaction.__enter__()
- return self.conn
- def __exit__(self, type_, value, traceback):
- try:
- self.transaction.__exit__(type_, value, traceback)
- finally:
- if not self.close_with_result:
- self.conn.close()
- def begin(self, close_with_result=False):
- """Return a context manager delivering a :class:`_engine.Connection`
- with a :class:`.Transaction` established.
- E.g.::
- with engine.begin() as conn:
- conn.execute(
- text("insert into table (x, y, z) values (1, 2, 3)")
- )
- conn.execute(text("my_special_procedure(5)"))
- Upon successful operation, the :class:`.Transaction`
- is committed. If an error is raised, the :class:`.Transaction`
- is rolled back.
- Legacy use only: the ``close_with_result`` flag is normally ``False``,
- and indicates that the :class:`_engine.Connection` will be closed when
- the operation is complete. When set to ``True``, it indicates the
- :class:`_engine.Connection` is in "single use" mode, where the
- :class:`_engine.CursorResult` returned by the first call to
- :meth:`_engine.Connection.execute` will close the
- :class:`_engine.Connection` when that :class:`_engine.CursorResult` has
- exhausted all result rows.
- .. seealso::
- :meth:`_engine.Engine.connect` - procure a
- :class:`_engine.Connection` from
- an :class:`_engine.Engine`.
- :meth:`_engine.Connection.begin` - start a :class:`.Transaction`
- for a particular :class:`_engine.Connection`.
- """
- if self._connection_cls._is_future:
- conn = self.connect()
- else:
- conn = self.connect(close_with_result=close_with_result)
- try:
- trans = conn.begin()
- except:
- with util.safe_reraise():
- conn.close()
- return Engine._trans_ctx(conn, trans, close_with_result)
- @util.deprecated(
- "1.4",
- "The :meth:`_engine.Engine.transaction` "
- "method is deprecated and will be "
- "removed in a future release. Use the :meth:`_engine.Engine.begin` "
- "context "
- "manager instead.",
- )
- def transaction(self, callable_, *args, **kwargs):
- r"""Execute the given function within a transaction boundary.
- The function is passed a :class:`_engine.Connection` newly procured
- from :meth:`_engine.Engine.connect` as the first argument,
- followed by the given \*args and \**kwargs.
- e.g.::
- def do_something(conn, x, y):
- conn.execute(text("some statement"), {'x':x, 'y':y})
- engine.transaction(do_something, 5, 10)
- The operations inside the function are all invoked within the
- context of a single :class:`.Transaction`.
- Upon success, the transaction is committed. If an
- exception is raised, the transaction is rolled back
- before propagating the exception.
- .. note::
- The :meth:`.transaction` method is superseded by
- the usage of the Python ``with:`` statement, which can
- be used with :meth:`_engine.Engine.begin`::
- with engine.begin() as conn:
- conn.execute(text("some statement"), {'x':5, 'y':10})
- .. seealso::
- :meth:`_engine.Engine.begin` - engine-level transactional
- context
- :meth:`_engine.Connection.transaction`
- - connection-level version of
- :meth:`_engine.Engine.transaction`
- """
- kwargs["_sa_skip_warning"] = True
- with self.connect() as conn:
- return conn.transaction(callable_, *args, **kwargs)
- @util.deprecated(
- "1.4",
- "The :meth:`_engine.Engine.run_callable` "
- "method is deprecated and will be "
- "removed in a future release. Use the :meth:`_engine.Engine.begin` "
- "context manager instead.",
- )
- def run_callable(self, callable_, *args, **kwargs):
- r"""Given a callable object or function, execute it, passing
- a :class:`_engine.Connection` as the first argument.
- The given \*args and \**kwargs are passed subsequent
- to the :class:`_engine.Connection` argument.
- This function, along with :meth:`_engine.Connection.run_callable`,
- allows a function to be run with a :class:`_engine.Connection`
- or :class:`_engine.Engine` object without the need to know
- which one is being dealt with.
- """
- kwargs["_sa_skip_warning"] = True
- with self.connect() as conn:
- return conn.run_callable(callable_, *args, **kwargs)
- def _run_ddl_visitor(self, visitorcallable, element, **kwargs):
- with self.begin() as conn:
- conn._run_ddl_visitor(visitorcallable, element, **kwargs)
- @util.deprecated_20(
- ":meth:`_engine.Engine.execute`",
- alternative="All statement execution in SQLAlchemy 2.0 is performed "
- "by the :meth:`_engine.Connection.execute` method of "
- ":class:`_engine.Connection`, "
- "or in the ORM by the :meth:`.Session.execute` method of "
- ":class:`.Session`.",
- )
- def execute(self, statement, *multiparams, **params):
- """Executes the given construct and returns a
- :class:`_engine.CursorResult`.
- The arguments are the same as those used by
- :meth:`_engine.Connection.execute`.
- Here, a :class:`_engine.Connection` is acquired using the
- :meth:`_engine.Engine.connect` method, and the statement executed
- with that connection. The returned :class:`_engine.CursorResult`
- is flagged
- such that when the :class:`_engine.CursorResult` is exhausted and its
- underlying cursor is closed, the :class:`_engine.Connection`
- created here
- will also be closed, which allows its associated DBAPI connection
- resource to be returned to the connection pool.
- """
- connection = self.connect(close_with_result=True)
- return connection.execute(statement, *multiparams, **params)
- @util.deprecated_20(
- ":meth:`_engine.Engine.scalar`",
- alternative="All statement execution in SQLAlchemy 2.0 is performed "
- "by the :meth:`_engine.Connection.execute` method of "
- ":class:`_engine.Connection`, "
- "or in the ORM by the :meth:`.Session.execute` method of "
- ":class:`.Session`; the :meth:`_future.Result.scalar` "
- "method can then be "
- "used to return a scalar result.",
- )
- def scalar(self, statement, *multiparams, **params):
- """Executes and returns the first column of the first row.
- The underlying result/cursor is closed after execution.
- """
- return self.execute(statement, *multiparams, **params).scalar()
- def _execute_clauseelement(
- self,
- elem,
- multiparams=None,
- params=None,
- execution_options=_EMPTY_EXECUTION_OPTS,
- ):
- connection = self.connect(close_with_result=True)
- return connection._execute_clauseelement(
- elem, multiparams, params, execution_options
- )
- def _execute_compiled(
- self,
- compiled,
- multiparams,
- params,
- execution_options=_EMPTY_EXECUTION_OPTS,
- ):
- connection = self.connect(close_with_result=True)
- return connection._execute_compiled(
- compiled, multiparams, params, execution_options
- )
- def connect(self, close_with_result=False):
- """Return a new :class:`_engine.Connection` object.
- The :class:`_engine.Connection` object is a facade that uses a DBAPI
- connection internally in order to communicate with the database. This
- connection is procured from the connection-holding :class:`_pool.Pool`
- referenced by this :class:`_engine.Engine`. When the
- :meth:`_engine.Connection.close` method of the
- :class:`_engine.Connection` object
- is called, the underlying DBAPI connection is then returned to the
- connection pool, where it may be used again in a subsequent call to
- :meth:`_engine.Engine.connect`.
- """
- return self._connection_cls(self, close_with_result=close_with_result)
- @util.deprecated(
- "1.4",
- "The :meth:`_engine.Engine.table_names` "
- "method is deprecated and will be "
- "removed in a future release. Please refer to "
- ":meth:`_reflection.Inspector.get_table_names`.",
- )
- def table_names(self, schema=None, connection=None):
- """Return a list of all table names available in the database.
- :param schema: Optional, retrieve names from a non-default schema.
- :param connection: Optional, use a specified connection.
- """
- with self._optional_conn_ctx_manager(connection) as conn:
- insp = inspection.inspect(conn)
- return insp.get_table_names(schema)
- @util.deprecated(
- "1.4",
- "The :meth:`_engine.Engine.has_table` "
- "method is deprecated and will be "
- "removed in a future release. Please refer to "
- ":meth:`_reflection.Inspector.has_table`.",
- )
- def has_table(self, table_name, schema=None):
- """Return True if the given backend has a table of the given name.
- .. seealso::
- :ref:`metadata_reflection_inspector` - detailed schema inspection
- using the :class:`_reflection.Inspector` interface.
- :class:`.quoted_name` - used to pass quoting information along
- with a schema identifier.
- """
- with self._optional_conn_ctx_manager(None) as conn:
- insp = inspection.inspect(conn)
- return insp.has_table(table_name, schema=schema)
- def _wrap_pool_connect(self, fn, connection):
- dialect = self.dialect
- try:
- return fn()
- except dialect.dbapi.Error as e:
- if connection is None:
- Connection._handle_dbapi_exception_noconnection(
- e, dialect, self
- )
- else:
- util.raise_(
- sys.exc_info()[1], with_traceback=sys.exc_info()[2]
- )
- def raw_connection(self, _connection=None):
- """Return a "raw" DBAPI connection from the connection pool.
- The returned object is a proxied version of the DBAPI
- connection object used by the underlying driver in use.
- The object will have all the same behavior as the real DBAPI
- connection, except that its ``close()`` method will result in the
- connection being returned to the pool, rather than being closed
- for real.
- This method provides direct DBAPI connection access for
- special situations when the API provided by
- :class:`_engine.Connection`
- is not needed. When a :class:`_engine.Connection` object is already
- present, the DBAPI connection is available using
- the :attr:`_engine.Connection.connection` accessor.
- .. seealso::
- :ref:`dbapi_connections`
- """
- return self._wrap_pool_connect(self.pool.connect, _connection)
- class OptionEngineMixin(object):
- _sa_propagate_class_events = False
- def __init__(self, proxied, execution_options):
- self._proxied = proxied
- self.url = proxied.url
- self.dialect = proxied.dialect
- self.logging_name = proxied.logging_name
- self.echo = proxied.echo
- self._compiled_cache = proxied._compiled_cache
- self.hide_parameters = proxied.hide_parameters
- log.instance_logger(self, echoflag=self.echo)
- # note: this will propagate events that are assigned to the parent
- # engine after this OptionEngine is created. Since we share
- # the events of the parent we also disallow class-level events
- # to apply to the OptionEngine class directly.
- #
- # the other way this can work would be to transfer existing
- # events only, using:
- # self.dispatch._update(proxied.dispatch)
- #
- # that might be more appropriate however it would be a behavioral
- # change for logic that assigns events to the parent engine and
- # would like it to take effect for the already-created sub-engine.
- self.dispatch = self.dispatch._join(proxied.dispatch)
- self._execution_options = proxied._execution_options
- self.update_execution_options(**execution_options)
- def _get_pool(self):
- return self._proxied.pool
- def _set_pool(self, pool):
- self._proxied.pool = pool
- pool = property(_get_pool, _set_pool)
- def _get_has_events(self):
- return self._proxied._has_events or self.__dict__.get(
- "_has_events", False
- )
- def _set_has_events(self, value):
- self.__dict__["_has_events"] = value
- _has_events = property(_get_has_events, _set_has_events)
- class OptionEngine(OptionEngineMixin, Engine):
- pass
- Engine._option_cls = OptionEngine
|