Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 

2383 rader
84 KiB

  1. # engine/default.py
  2. # Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: https://www.opensource.org/licenses/mit-license.php
  7. # mypy: allow-untyped-defs, allow-untyped-calls
  8. """Default implementations of per-dialect sqlalchemy.engine classes.
  9. These are semi-private implementation classes which are only of importance
  10. to database dialect authors; dialects will usually use the classes here
  11. as the base class for their own corresponding classes.
  12. """
  13. from __future__ import annotations
  14. import functools
  15. import operator
  16. import random
  17. import re
  18. from time import perf_counter
  19. import typing
  20. from typing import Any
  21. from typing import Callable
  22. from typing import cast
  23. from typing import Dict
  24. from typing import List
  25. from typing import Mapping
  26. from typing import MutableMapping
  27. from typing import MutableSequence
  28. from typing import Optional
  29. from typing import Sequence
  30. from typing import Set
  31. from typing import Tuple
  32. from typing import Type
  33. from typing import TYPE_CHECKING
  34. from typing import Union
  35. import weakref
  36. from . import characteristics
  37. from . import cursor as _cursor
  38. from . import interfaces
  39. from .base import Connection
  40. from .interfaces import CacheStats
  41. from .interfaces import DBAPICursor
  42. from .interfaces import Dialect
  43. from .interfaces import ExecuteStyle
  44. from .interfaces import ExecutionContext
  45. from .reflection import ObjectKind
  46. from .reflection import ObjectScope
  47. from .. import event
  48. from .. import exc
  49. from .. import pool
  50. from .. import util
  51. from ..sql import compiler
  52. from ..sql import dml
  53. from ..sql import expression
  54. from ..sql import type_api
  55. from ..sql import util as sql_util
  56. from ..sql._typing import is_tuple_type
  57. from ..sql.base import _NoArg
  58. from ..sql.compiler import DDLCompiler
  59. from ..sql.compiler import InsertmanyvaluesSentinelOpts
  60. from ..sql.compiler import SQLCompiler
  61. from ..sql.elements import quoted_name
  62. from ..util.typing import Final
  63. from ..util.typing import Literal
  64. if typing.TYPE_CHECKING:
  65. from types import ModuleType
  66. from .base import Engine
  67. from .cursor import ResultFetchStrategy
  68. from .interfaces import _CoreMultiExecuteParams
  69. from .interfaces import _CoreSingleExecuteParams
  70. from .interfaces import _DBAPICursorDescription
  71. from .interfaces import _DBAPIMultiExecuteParams
  72. from .interfaces import _DBAPISingleExecuteParams
  73. from .interfaces import _ExecuteOptions
  74. from .interfaces import _MutableCoreSingleExecuteParams
  75. from .interfaces import _ParamStyle
  76. from .interfaces import ConnectArgsType
  77. from .interfaces import DBAPIConnection
  78. from .interfaces import DBAPIModule
  79. from .interfaces import IsolationLevel
  80. from .row import Row
  81. from .url import URL
  82. from ..event import _ListenerFnType
  83. from ..pool import Pool
  84. from ..pool import PoolProxiedConnection
  85. from ..sql import Executable
  86. from ..sql.compiler import Compiled
  87. from ..sql.compiler import Linting
  88. from ..sql.compiler import ResultColumnsEntry
  89. from ..sql.dml import DMLState
  90. from ..sql.dml import UpdateBase
  91. from ..sql.elements import BindParameter
  92. from ..sql.schema import Column
  93. from ..sql.type_api import _BindProcessorType
  94. from ..sql.type_api import _ResultProcessorType
  95. from ..sql.type_api import TypeEngine
  96. # When we're handed literal SQL, ensure it's a SELECT query
  97. SERVER_SIDE_CURSOR_RE = re.compile(r"\s*SELECT", re.I | re.UNICODE)
  98. (
  99. CACHE_HIT,
  100. CACHE_MISS,
  101. CACHING_DISABLED,
  102. NO_CACHE_KEY,
  103. NO_DIALECT_SUPPORT,
  104. ) = list(CacheStats)
  105. class DefaultDialect(Dialect):
  106. """Default implementation of Dialect"""
  107. statement_compiler = compiler.SQLCompiler
  108. ddl_compiler = compiler.DDLCompiler
  109. type_compiler_cls = compiler.GenericTypeCompiler
  110. preparer = compiler.IdentifierPreparer
  111. supports_alter = True
  112. supports_comments = False
  113. supports_constraint_comments = False
  114. inline_comments = False
  115. supports_statement_cache = True
  116. div_is_floordiv = True
  117. bind_typing = interfaces.BindTyping.NONE
  118. include_set_input_sizes: Optional[Set[Any]] = None
  119. exclude_set_input_sizes: Optional[Set[Any]] = None
  120. # the first value we'd get for an autoincrement column.
  121. default_sequence_base = 1
  122. # most DBAPIs happy with this for execute().
  123. # not cx_oracle.
  124. execute_sequence_format = tuple
  125. supports_schemas = True
  126. supports_views = True
  127. supports_sequences = False
  128. sequences_optional = False
  129. preexecute_autoincrement_sequences = False
  130. supports_identity_columns = False
  131. postfetch_lastrowid = True
  132. favor_returning_over_lastrowid = False
  133. insert_null_pk_still_autoincrements = False
  134. update_returning = False
  135. delete_returning = False
  136. update_returning_multifrom = False
  137. delete_returning_multifrom = False
  138. insert_returning = False
  139. cte_follows_insert = False
  140. supports_native_enum = False
  141. supports_native_boolean = False
  142. supports_native_uuid = False
  143. returns_native_bytes = False
  144. non_native_boolean_check_constraint = True
  145. supports_simple_order_by_label = True
  146. tuple_in_values = False
  147. connection_characteristics = util.immutabledict(
  148. {
  149. "isolation_level": characteristics.IsolationLevelCharacteristic(),
  150. "logging_token": characteristics.LoggingTokenCharacteristic(),
  151. }
  152. )
  153. engine_config_types: Mapping[str, Any] = util.immutabledict(
  154. {
  155. "pool_timeout": util.asint,
  156. "echo": util.bool_or_str("debug"),
  157. "echo_pool": util.bool_or_str("debug"),
  158. "pool_recycle": util.asint,
  159. "pool_size": util.asint,
  160. "max_overflow": util.asint,
  161. "future": util.asbool,
  162. }
  163. )
  164. # if the NUMERIC type
  165. # returns decimal.Decimal.
  166. # *not* the FLOAT type however.
  167. supports_native_decimal = False
  168. name = "default"
  169. # length at which to truncate
  170. # any identifier.
  171. max_identifier_length = 9999
  172. _user_defined_max_identifier_length: Optional[int] = None
  173. isolation_level: Optional[str] = None
  174. # sub-categories of max_identifier_length.
  175. # currently these accommodate for MySQL which allows alias names
  176. # of 255 but DDL names only of 64.
  177. max_index_name_length: Optional[int] = None
  178. max_constraint_name_length: Optional[int] = None
  179. supports_sane_rowcount = True
  180. supports_sane_multi_rowcount = True
  181. colspecs: MutableMapping[Type[TypeEngine[Any]], Type[TypeEngine[Any]]] = {}
  182. default_paramstyle = "named"
  183. supports_default_values = False
  184. """dialect supports INSERT... DEFAULT VALUES syntax"""
  185. supports_default_metavalue = False
  186. """dialect supports INSERT... VALUES (DEFAULT) syntax"""
  187. default_metavalue_token = "DEFAULT"
  188. """for INSERT... VALUES (DEFAULT) syntax, the token to put in the
  189. parenthesis."""
  190. # not sure if this is a real thing but the compiler will deliver it
  191. # if this is the only flag enabled.
  192. supports_empty_insert = True
  193. """dialect supports INSERT () VALUES ()"""
  194. supports_multivalues_insert = False
  195. use_insertmanyvalues: bool = False
  196. use_insertmanyvalues_wo_returning: bool = False
  197. insertmanyvalues_implicit_sentinel: InsertmanyvaluesSentinelOpts = (
  198. InsertmanyvaluesSentinelOpts.NOT_SUPPORTED
  199. )
  200. insertmanyvalues_page_size: int = 1000
  201. insertmanyvalues_max_parameters = 32700
  202. supports_is_distinct_from = True
  203. supports_server_side_cursors = False
  204. server_side_cursors = False
  205. # extra record-level locking features (#4860)
  206. supports_for_update_of = False
  207. server_version_info = None
  208. default_schema_name: Optional[str] = None
  209. # indicates symbol names are
  210. # UPPERCASED if they are case insensitive
  211. # within the database.
  212. # if this is True, the methods normalize_name()
  213. # and denormalize_name() must be provided.
  214. requires_name_normalize = False
  215. is_async = False
  216. has_terminate = False
  217. # TODO: this is not to be part of 2.0. implement rudimentary binary
  218. # literals for SQLite, PostgreSQL, MySQL only within
  219. # _Binary.literal_processor
  220. _legacy_binary_type_literal_encoding = "utf-8"
  221. @util.deprecated_params(
  222. empty_in_strategy=(
  223. "1.4",
  224. "The :paramref:`_sa.create_engine.empty_in_strategy` keyword is "
  225. "deprecated, and no longer has any effect. All IN expressions "
  226. "are now rendered using "
  227. 'the "expanding parameter" strategy which renders a set of bound'
  228. 'expressions, or an "empty set" SELECT, at statement execution'
  229. "time.",
  230. ),
  231. server_side_cursors=(
  232. "1.4",
  233. "The :paramref:`_sa.create_engine.server_side_cursors` parameter "
  234. "is deprecated and will be removed in a future release. Please "
  235. "use the "
  236. ":paramref:`_engine.Connection.execution_options.stream_results` "
  237. "parameter.",
  238. ),
  239. )
  240. def __init__(
  241. self,
  242. paramstyle: Optional[_ParamStyle] = None,
  243. isolation_level: Optional[IsolationLevel] = None,
  244. dbapi: Optional[ModuleType] = None,
  245. implicit_returning: Literal[True] = True,
  246. supports_native_boolean: Optional[bool] = None,
  247. max_identifier_length: Optional[int] = None,
  248. label_length: Optional[int] = None,
  249. insertmanyvalues_page_size: Union[_NoArg, int] = _NoArg.NO_ARG,
  250. use_insertmanyvalues: Optional[bool] = None,
  251. # util.deprecated_params decorator cannot render the
  252. # Linting.NO_LINTING constant
  253. compiler_linting: Linting = int(compiler.NO_LINTING), # type: ignore
  254. server_side_cursors: bool = False,
  255. **kwargs: Any,
  256. ):
  257. if server_side_cursors:
  258. if not self.supports_server_side_cursors:
  259. raise exc.ArgumentError(
  260. "Dialect %s does not support server side cursors" % self
  261. )
  262. else:
  263. self.server_side_cursors = True
  264. if getattr(self, "use_setinputsizes", False):
  265. util.warn_deprecated(
  266. "The dialect-level use_setinputsizes attribute is "
  267. "deprecated. Please use "
  268. "bind_typing = BindTyping.SETINPUTSIZES",
  269. "2.0",
  270. )
  271. self.bind_typing = interfaces.BindTyping.SETINPUTSIZES
  272. self.positional = False
  273. self._ischema = None
  274. self.dbapi = dbapi
  275. if paramstyle is not None:
  276. self.paramstyle = paramstyle
  277. elif self.dbapi is not None:
  278. self.paramstyle = self.dbapi.paramstyle
  279. else:
  280. self.paramstyle = self.default_paramstyle
  281. self.positional = self.paramstyle in (
  282. "qmark",
  283. "format",
  284. "numeric",
  285. "numeric_dollar",
  286. )
  287. self.identifier_preparer = self.preparer(self)
  288. self._on_connect_isolation_level = isolation_level
  289. legacy_tt_callable = getattr(self, "type_compiler", None)
  290. if legacy_tt_callable is not None:
  291. tt_callable = cast(
  292. Type[compiler.GenericTypeCompiler],
  293. self.type_compiler,
  294. )
  295. else:
  296. tt_callable = self.type_compiler_cls
  297. self.type_compiler_instance = self.type_compiler = tt_callable(self)
  298. if supports_native_boolean is not None:
  299. self.supports_native_boolean = supports_native_boolean
  300. self._user_defined_max_identifier_length = max_identifier_length
  301. if self._user_defined_max_identifier_length:
  302. self.max_identifier_length = (
  303. self._user_defined_max_identifier_length
  304. )
  305. self.label_length = label_length
  306. self.compiler_linting = compiler_linting
  307. if use_insertmanyvalues is not None:
  308. self.use_insertmanyvalues = use_insertmanyvalues
  309. if insertmanyvalues_page_size is not _NoArg.NO_ARG:
  310. self.insertmanyvalues_page_size = insertmanyvalues_page_size
  311. @property
  312. @util.deprecated(
  313. "2.0",
  314. "full_returning is deprecated, please use insert_returning, "
  315. "update_returning, delete_returning",
  316. )
  317. def full_returning(self):
  318. return (
  319. self.insert_returning
  320. and self.update_returning
  321. and self.delete_returning
  322. )
  323. @util.memoized_property
  324. def insert_executemany_returning(self):
  325. """Default implementation for insert_executemany_returning, if not
  326. otherwise overridden by the specific dialect.
  327. The default dialect determines "insert_executemany_returning" is
  328. available if the dialect in use has opted into using the
  329. "use_insertmanyvalues" feature. If they haven't opted into that, then
  330. this attribute is False, unless the dialect in question overrides this
  331. and provides some other implementation (such as the Oracle Database
  332. dialects).
  333. """
  334. return self.insert_returning and self.use_insertmanyvalues
  335. @util.memoized_property
  336. def insert_executemany_returning_sort_by_parameter_order(self):
  337. """Default implementation for
  338. insert_executemany_returning_deterministic_order, if not otherwise
  339. overridden by the specific dialect.
  340. The default dialect determines "insert_executemany_returning" can have
  341. deterministic order only if the dialect in use has opted into using the
  342. "use_insertmanyvalues" feature, which implements deterministic ordering
  343. using client side sentinel columns only by default. The
  344. "insertmanyvalues" feature also features alternate forms that can
  345. use server-generated PK values as "sentinels", but those are only
  346. used if the :attr:`.Dialect.insertmanyvalues_implicit_sentinel`
  347. bitflag enables those alternate SQL forms, which are disabled
  348. by default.
  349. If the dialect in use hasn't opted into that, then this attribute is
  350. False, unless the dialect in question overrides this and provides some
  351. other implementation (such as the Oracle Database dialects).
  352. """
  353. return self.insert_returning and self.use_insertmanyvalues
  354. update_executemany_returning = False
  355. delete_executemany_returning = False
  356. @util.memoized_property
  357. def loaded_dbapi(self) -> DBAPIModule:
  358. if self.dbapi is None:
  359. raise exc.InvalidRequestError(
  360. f"Dialect {self} does not have a Python DBAPI established "
  361. "and cannot be used for actual database interaction"
  362. )
  363. return self.dbapi
  364. @util.memoized_property
  365. def _bind_typing_render_casts(self):
  366. return self.bind_typing is interfaces.BindTyping.RENDER_CASTS
  367. def _ensure_has_table_connection(self, arg: Connection) -> None:
  368. if not isinstance(arg, Connection):
  369. raise exc.ArgumentError(
  370. "The argument passed to Dialect.has_table() should be a "
  371. "%s, got %s. "
  372. "Additionally, the Dialect.has_table() method is for "
  373. "internal dialect "
  374. "use only; please use "
  375. "``inspect(some_engine).has_table(<tablename>>)`` "
  376. "for public API use." % (Connection, type(arg))
  377. )
  378. @util.memoized_property
  379. def _supports_statement_cache(self):
  380. ssc = self.__class__.__dict__.get("supports_statement_cache", None)
  381. if ssc is None:
  382. util.warn(
  383. "Dialect %s:%s will not make use of SQL compilation caching "
  384. "as it does not set the 'supports_statement_cache' attribute "
  385. "to ``True``. This can have "
  386. "significant performance implications including some "
  387. "performance degradations in comparison to prior SQLAlchemy "
  388. "versions. Dialect maintainers should seek to set this "
  389. "attribute to True after appropriate development and testing "
  390. "for SQLAlchemy 1.4 caching support. Alternatively, this "
  391. "attribute may be set to False which will disable this "
  392. "warning." % (self.name, self.driver),
  393. code="cprf",
  394. )
  395. return bool(ssc)
  396. @util.memoized_property
  397. def _type_memos(self):
  398. return weakref.WeakKeyDictionary()
  399. @property
  400. def dialect_description(self): # type: ignore[override]
  401. return self.name + "+" + self.driver
  402. @property
  403. def supports_sane_rowcount_returning(self):
  404. """True if this dialect supports sane rowcount even if RETURNING is
  405. in use.
  406. For dialects that don't support RETURNING, this is synonymous with
  407. ``supports_sane_rowcount``.
  408. """
  409. return self.supports_sane_rowcount
  410. @classmethod
  411. def get_pool_class(cls, url: URL) -> Type[Pool]:
  412. return getattr(cls, "poolclass", pool.QueuePool)
  413. def get_dialect_pool_class(self, url: URL) -> Type[Pool]:
  414. return self.get_pool_class(url)
  415. @classmethod
  416. def load_provisioning(cls):
  417. package = ".".join(cls.__module__.split(".")[0:-1])
  418. try:
  419. __import__(package + ".provision")
  420. except ImportError:
  421. pass
  422. def _builtin_onconnect(self) -> Optional[_ListenerFnType]:
  423. if self._on_connect_isolation_level is not None:
  424. def builtin_connect(dbapi_conn, conn_rec):
  425. self._assert_and_set_isolation_level(
  426. dbapi_conn, self._on_connect_isolation_level
  427. )
  428. return builtin_connect
  429. else:
  430. return None
  431. def initialize(self, connection: Connection) -> None:
  432. try:
  433. self.server_version_info = self._get_server_version_info(
  434. connection
  435. )
  436. except NotImplementedError:
  437. self.server_version_info = None
  438. try:
  439. self.default_schema_name = self._get_default_schema_name(
  440. connection
  441. )
  442. except NotImplementedError:
  443. self.default_schema_name = None
  444. try:
  445. self.default_isolation_level = self.get_default_isolation_level(
  446. connection.connection.dbapi_connection
  447. )
  448. except NotImplementedError:
  449. self.default_isolation_level = None
  450. if not self._user_defined_max_identifier_length:
  451. max_ident_length = self._check_max_identifier_length(connection)
  452. if max_ident_length:
  453. self.max_identifier_length = max_ident_length
  454. if (
  455. self.label_length
  456. and self.label_length > self.max_identifier_length
  457. ):
  458. raise exc.ArgumentError(
  459. "Label length of %d is greater than this dialect's"
  460. " maximum identifier length of %d"
  461. % (self.label_length, self.max_identifier_length)
  462. )
  463. def on_connect(self) -> Optional[Callable[[Any], None]]:
  464. # inherits the docstring from interfaces.Dialect.on_connect
  465. return None
  466. def _check_max_identifier_length(self, connection):
  467. """Perform a connection / server version specific check to determine
  468. the max_identifier_length.
  469. If the dialect's class level max_identifier_length should be used,
  470. can return None.
  471. .. versionadded:: 1.3.9
  472. """
  473. return None
  474. def get_default_isolation_level(self, dbapi_conn):
  475. """Given a DBAPI connection, return its isolation level, or
  476. a default isolation level if one cannot be retrieved.
  477. May be overridden by subclasses in order to provide a
  478. "fallback" isolation level for databases that cannot reliably
  479. retrieve the actual isolation level.
  480. By default, calls the :meth:`_engine.Interfaces.get_isolation_level`
  481. method, propagating any exceptions raised.
  482. .. versionadded:: 1.3.22
  483. """
  484. return self.get_isolation_level(dbapi_conn)
  485. def type_descriptor(self, typeobj):
  486. """Provide a database-specific :class:`.TypeEngine` object, given
  487. the generic object which comes from the types module.
  488. This method looks for a dictionary called
  489. ``colspecs`` as a class or instance-level variable,
  490. and passes on to :func:`_types.adapt_type`.
  491. """
  492. return type_api.adapt_type(typeobj, self.colspecs)
  493. def has_index(self, connection, table_name, index_name, schema=None, **kw):
  494. if not self.has_table(connection, table_name, schema=schema, **kw):
  495. return False
  496. for idx in self.get_indexes(
  497. connection, table_name, schema=schema, **kw
  498. ):
  499. if idx["name"] == index_name:
  500. return True
  501. else:
  502. return False
  503. def has_schema(
  504. self, connection: Connection, schema_name: str, **kw: Any
  505. ) -> bool:
  506. return schema_name in self.get_schema_names(connection, **kw)
  507. def validate_identifier(self, ident: str) -> None:
  508. if len(ident) > self.max_identifier_length:
  509. raise exc.IdentifierError(
  510. "Identifier '%s' exceeds maximum length of %d characters"
  511. % (ident, self.max_identifier_length)
  512. )
  513. def connect(self, *cargs: Any, **cparams: Any) -> DBAPIConnection:
  514. # inherits the docstring from interfaces.Dialect.connect
  515. return self.loaded_dbapi.connect(*cargs, **cparams) # type: ignore[no-any-return] # NOQA: E501
  516. def create_connect_args(self, url: URL) -> ConnectArgsType:
  517. # inherits the docstring from interfaces.Dialect.create_connect_args
  518. opts = url.translate_connect_args()
  519. opts.update(url.query)
  520. return ([], opts)
  521. def set_engine_execution_options(
  522. self, engine: Engine, opts: Mapping[str, Any]
  523. ) -> None:
  524. supported_names = set(self.connection_characteristics).intersection(
  525. opts
  526. )
  527. if supported_names:
  528. characteristics: Mapping[str, Any] = util.immutabledict(
  529. (name, opts[name]) for name in supported_names
  530. )
  531. @event.listens_for(engine, "engine_connect")
  532. def set_connection_characteristics(connection):
  533. self._set_connection_characteristics(
  534. connection, characteristics
  535. )
  536. def set_connection_execution_options(
  537. self, connection: Connection, opts: Mapping[str, Any]
  538. ) -> None:
  539. supported_names = set(self.connection_characteristics).intersection(
  540. opts
  541. )
  542. if supported_names:
  543. characteristics: Mapping[str, Any] = util.immutabledict(
  544. (name, opts[name]) for name in supported_names
  545. )
  546. self._set_connection_characteristics(connection, characteristics)
  547. def _set_connection_characteristics(self, connection, characteristics):
  548. characteristic_values = [
  549. (name, self.connection_characteristics[name], value)
  550. for name, value in characteristics.items()
  551. ]
  552. if connection.in_transaction():
  553. trans_objs = [
  554. (name, obj)
  555. for name, obj, _ in characteristic_values
  556. if obj.transactional
  557. ]
  558. if trans_objs:
  559. raise exc.InvalidRequestError(
  560. "This connection has already initialized a SQLAlchemy "
  561. "Transaction() object via begin() or autobegin; "
  562. "%s may not be altered unless rollback() or commit() "
  563. "is called first."
  564. % (", ".join(name for name, obj in trans_objs))
  565. )
  566. dbapi_connection = connection.connection.dbapi_connection
  567. for _, characteristic, value in characteristic_values:
  568. characteristic.set_connection_characteristic(
  569. self, connection, dbapi_connection, value
  570. )
  571. connection.connection._connection_record.finalize_callback.append(
  572. functools.partial(self._reset_characteristics, characteristics)
  573. )
  574. def _reset_characteristics(self, characteristics, dbapi_connection):
  575. for characteristic_name in characteristics:
  576. characteristic = self.connection_characteristics[
  577. characteristic_name
  578. ]
  579. characteristic.reset_characteristic(self, dbapi_connection)
  580. def do_begin(self, dbapi_connection):
  581. pass
  582. def do_rollback(self, dbapi_connection):
  583. dbapi_connection.rollback()
  584. def do_commit(self, dbapi_connection):
  585. dbapi_connection.commit()
  586. def do_terminate(self, dbapi_connection):
  587. self.do_close(dbapi_connection)
  588. def do_close(self, dbapi_connection):
  589. dbapi_connection.close()
  590. @util.memoized_property
  591. def _dialect_specific_select_one(self):
  592. return str(expression.select(1).compile(dialect=self))
  593. def _do_ping_w_event(self, dbapi_connection: DBAPIConnection) -> bool:
  594. try:
  595. return self.do_ping(dbapi_connection)
  596. except self.loaded_dbapi.Error as err:
  597. is_disconnect = self.is_disconnect(err, dbapi_connection, None)
  598. if self._has_events:
  599. try:
  600. Connection._handle_dbapi_exception_noconnection(
  601. err,
  602. self,
  603. is_disconnect=is_disconnect,
  604. invalidate_pool_on_disconnect=False,
  605. is_pre_ping=True,
  606. )
  607. except exc.StatementError as new_err:
  608. is_disconnect = new_err.connection_invalidated
  609. if is_disconnect:
  610. return False
  611. else:
  612. raise
  613. def do_ping(self, dbapi_connection: DBAPIConnection) -> bool:
  614. cursor = dbapi_connection.cursor()
  615. try:
  616. cursor.execute(self._dialect_specific_select_one)
  617. finally:
  618. cursor.close()
  619. return True
  620. def create_xid(self):
  621. """Create a random two-phase transaction ID.
  622. This id will be passed to do_begin_twophase(), do_rollback_twophase(),
  623. do_commit_twophase(). Its format is unspecified.
  624. """
  625. return "_sa_%032x" % random.randint(0, 2**128)
  626. def do_savepoint(self, connection, name):
  627. connection.execute(expression.SavepointClause(name))
  628. def do_rollback_to_savepoint(self, connection, name):
  629. connection.execute(expression.RollbackToSavepointClause(name))
  630. def do_release_savepoint(self, connection, name):
  631. connection.execute(expression.ReleaseSavepointClause(name))
  632. def _deliver_insertmanyvalues_batches(
  633. self,
  634. connection,
  635. cursor,
  636. statement,
  637. parameters,
  638. generic_setinputsizes,
  639. context,
  640. ):
  641. context = cast(DefaultExecutionContext, context)
  642. compiled = cast(SQLCompiler, context.compiled)
  643. _composite_sentinel_proc: Sequence[
  644. Optional[_ResultProcessorType[Any]]
  645. ] = ()
  646. _scalar_sentinel_proc: Optional[_ResultProcessorType[Any]] = None
  647. _sentinel_proc_initialized: bool = False
  648. compiled_parameters = context.compiled_parameters
  649. imv = compiled._insertmanyvalues
  650. assert imv is not None
  651. is_returning: Final[bool] = bool(compiled.effective_returning)
  652. batch_size = context.execution_options.get(
  653. "insertmanyvalues_page_size", self.insertmanyvalues_page_size
  654. )
  655. if compiled.schema_translate_map:
  656. schema_translate_map = context.execution_options.get(
  657. "schema_translate_map", {}
  658. )
  659. else:
  660. schema_translate_map = None
  661. if is_returning:
  662. result: Optional[List[Any]] = []
  663. context._insertmanyvalues_rows = result
  664. sort_by_parameter_order = imv.sort_by_parameter_order
  665. else:
  666. sort_by_parameter_order = False
  667. result = None
  668. for imv_batch in compiled._deliver_insertmanyvalues_batches(
  669. statement,
  670. parameters,
  671. compiled_parameters,
  672. generic_setinputsizes,
  673. batch_size,
  674. sort_by_parameter_order,
  675. schema_translate_map,
  676. ):
  677. yield imv_batch
  678. if is_returning:
  679. try:
  680. rows = context.fetchall_for_returning(cursor)
  681. except BaseException as be:
  682. connection._handle_dbapi_exception(
  683. be,
  684. sql_util._long_statement(imv_batch.replaced_statement),
  685. imv_batch.replaced_parameters,
  686. None,
  687. context,
  688. is_sub_exec=True,
  689. )
  690. # I would have thought "is_returning: Final[bool]"
  691. # would have assured this but pylance thinks not
  692. assert result is not None
  693. if imv.num_sentinel_columns and not imv_batch.is_downgraded:
  694. composite_sentinel = imv.num_sentinel_columns > 1
  695. if imv.implicit_sentinel:
  696. # for implicit sentinel, which is currently single-col
  697. # integer autoincrement, do a simple sort.
  698. assert not composite_sentinel
  699. result.extend(
  700. sorted(rows, key=operator.itemgetter(-1))
  701. )
  702. continue
  703. # otherwise, create dictionaries to match up batches
  704. # with parameters
  705. assert imv.sentinel_param_keys
  706. assert imv.sentinel_columns
  707. _nsc = imv.num_sentinel_columns
  708. if not _sentinel_proc_initialized:
  709. if composite_sentinel:
  710. _composite_sentinel_proc = [
  711. col.type._cached_result_processor(
  712. self, cursor_desc[1]
  713. )
  714. for col, cursor_desc in zip(
  715. imv.sentinel_columns,
  716. cursor.description[-_nsc:],
  717. )
  718. ]
  719. else:
  720. _scalar_sentinel_proc = (
  721. imv.sentinel_columns[0]
  722. ).type._cached_result_processor(
  723. self, cursor.description[-1][1]
  724. )
  725. _sentinel_proc_initialized = True
  726. rows_by_sentinel: Union[
  727. Dict[Tuple[Any, ...], Any],
  728. Dict[Any, Any],
  729. ]
  730. if composite_sentinel:
  731. rows_by_sentinel = {
  732. tuple(
  733. (proc(val) if proc else val)
  734. for val, proc in zip(
  735. row[-_nsc:], _composite_sentinel_proc
  736. )
  737. ): row
  738. for row in rows
  739. }
  740. elif _scalar_sentinel_proc:
  741. rows_by_sentinel = {
  742. _scalar_sentinel_proc(row[-1]): row for row in rows
  743. }
  744. else:
  745. rows_by_sentinel = {row[-1]: row for row in rows}
  746. if len(rows_by_sentinel) != len(imv_batch.batch):
  747. # see test_insert_exec.py::
  748. # IMVSentinelTest::test_sentinel_incorrect_rowcount
  749. # for coverage / demonstration
  750. raise exc.InvalidRequestError(
  751. f"Sentinel-keyed result set did not produce "
  752. f"correct number of rows {len(imv_batch.batch)}; "
  753. "produced "
  754. f"{len(rows_by_sentinel)}. Please ensure the "
  755. "sentinel column is fully unique and populated in "
  756. "all cases."
  757. )
  758. try:
  759. ordered_rows = [
  760. rows_by_sentinel[sentinel_keys]
  761. for sentinel_keys in imv_batch.sentinel_values
  762. ]
  763. except KeyError as ke:
  764. # see test_insert_exec.py::
  765. # IMVSentinelTest::test_sentinel_cant_match_keys
  766. # for coverage / demonstration
  767. raise exc.InvalidRequestError(
  768. f"Can't match sentinel values in result set to "
  769. f"parameter sets; key {ke.args[0]!r} was not "
  770. "found. "
  771. "There may be a mismatch between the datatype "
  772. "passed to the DBAPI driver vs. that which it "
  773. "returns in a result row. Ensure the given "
  774. "Python value matches the expected result type "
  775. "*exactly*, taking care to not rely upon implicit "
  776. "conversions which may occur such as when using "
  777. "strings in place of UUID or integer values, etc. "
  778. ) from ke
  779. result.extend(ordered_rows)
  780. else:
  781. result.extend(rows)
  782. def do_executemany(self, cursor, statement, parameters, context=None):
  783. cursor.executemany(statement, parameters)
  784. def do_execute(self, cursor, statement, parameters, context=None):
  785. cursor.execute(statement, parameters)
  786. def do_execute_no_params(self, cursor, statement, context=None):
  787. cursor.execute(statement)
  788. def is_disconnect(
  789. self,
  790. e: DBAPIModule.Error,
  791. connection: Union[
  792. pool.PoolProxiedConnection, interfaces.DBAPIConnection, None
  793. ],
  794. cursor: Optional[interfaces.DBAPICursor],
  795. ) -> bool:
  796. return False
  797. @util.memoized_instancemethod
  798. def _gen_allowed_isolation_levels(self, dbapi_conn):
  799. try:
  800. raw_levels = list(self.get_isolation_level_values(dbapi_conn))
  801. except NotImplementedError:
  802. return None
  803. else:
  804. normalized_levels = [
  805. level.replace("_", " ").upper() for level in raw_levels
  806. ]
  807. if raw_levels != normalized_levels:
  808. raise ValueError(
  809. f"Dialect {self.name!r} get_isolation_level_values() "
  810. f"method should return names as UPPERCASE using spaces, "
  811. f"not underscores; got "
  812. f"{sorted(set(raw_levels).difference(normalized_levels))}"
  813. )
  814. return tuple(normalized_levels)
  815. def _assert_and_set_isolation_level(self, dbapi_conn, level):
  816. level = level.replace("_", " ").upper()
  817. _allowed_isolation_levels = self._gen_allowed_isolation_levels(
  818. dbapi_conn
  819. )
  820. if (
  821. _allowed_isolation_levels
  822. and level not in _allowed_isolation_levels
  823. ):
  824. raise exc.ArgumentError(
  825. f"Invalid value {level!r} for isolation_level. "
  826. f"Valid isolation levels for {self.name!r} are "
  827. f"{', '.join(_allowed_isolation_levels)}"
  828. )
  829. self.set_isolation_level(dbapi_conn, level)
  830. def reset_isolation_level(self, dbapi_conn):
  831. if self._on_connect_isolation_level is not None:
  832. assert (
  833. self._on_connect_isolation_level == "AUTOCOMMIT"
  834. or self._on_connect_isolation_level
  835. == self.default_isolation_level
  836. )
  837. self._assert_and_set_isolation_level(
  838. dbapi_conn, self._on_connect_isolation_level
  839. )
  840. else:
  841. assert self.default_isolation_level is not None
  842. self._assert_and_set_isolation_level(
  843. dbapi_conn,
  844. self.default_isolation_level,
  845. )
  846. def normalize_name(self, name):
  847. if name is None:
  848. return None
  849. name_lower = name.lower()
  850. name_upper = name.upper()
  851. if name_upper == name_lower:
  852. # name has no upper/lower conversion, e.g. non-european characters.
  853. # return unchanged
  854. return name
  855. elif name_upper == name and not (
  856. self.identifier_preparer._requires_quotes
  857. )(name_lower):
  858. # name is all uppercase and doesn't require quoting; normalize
  859. # to all lower case
  860. return name_lower
  861. elif name_lower == name:
  862. # name is all lower case, which if denormalized means we need to
  863. # force quoting on it
  864. return quoted_name(name, quote=True)
  865. else:
  866. # name is mixed case, means it will be quoted in SQL when used
  867. # later, no normalizes
  868. return name
  869. def denormalize_name(self, name):
  870. if name is None:
  871. return None
  872. name_lower = name.lower()
  873. name_upper = name.upper()
  874. if name_upper == name_lower:
  875. # name has no upper/lower conversion, e.g. non-european characters.
  876. # return unchanged
  877. return name
  878. elif name_lower == name and not (
  879. self.identifier_preparer._requires_quotes
  880. )(name_lower):
  881. name = name_upper
  882. return name
  883. def get_driver_connection(self, connection: DBAPIConnection) -> Any:
  884. return connection
  885. def _overrides_default(self, method):
  886. return (
  887. getattr(type(self), method).__code__
  888. is not getattr(DefaultDialect, method).__code__
  889. )
  890. def _default_multi_reflect(
  891. self,
  892. single_tbl_method,
  893. connection,
  894. kind,
  895. schema,
  896. filter_names,
  897. scope,
  898. **kw,
  899. ):
  900. names_fns = []
  901. temp_names_fns = []
  902. if ObjectKind.TABLE in kind:
  903. names_fns.append(self.get_table_names)
  904. temp_names_fns.append(self.get_temp_table_names)
  905. if ObjectKind.VIEW in kind:
  906. names_fns.append(self.get_view_names)
  907. temp_names_fns.append(self.get_temp_view_names)
  908. if ObjectKind.MATERIALIZED_VIEW in kind:
  909. names_fns.append(self.get_materialized_view_names)
  910. # no temp materialized view at the moment
  911. # temp_names_fns.append(self.get_temp_materialized_view_names)
  912. unreflectable = kw.pop("unreflectable", {})
  913. if (
  914. filter_names
  915. and scope is ObjectScope.ANY
  916. and kind is ObjectKind.ANY
  917. ):
  918. # if names are given and no qualification on type of table
  919. # (i.e. the Table(..., autoload) case), take the names as given,
  920. # don't run names queries. If a table does not exit
  921. # NoSuchTableError is raised and it's skipped
  922. # this also suits the case for mssql where we can reflect
  923. # individual temp tables but there's no temp_names_fn
  924. names = filter_names
  925. else:
  926. names = []
  927. name_kw = {"schema": schema, **kw}
  928. fns = []
  929. if ObjectScope.DEFAULT in scope:
  930. fns.extend(names_fns)
  931. if ObjectScope.TEMPORARY in scope:
  932. fns.extend(temp_names_fns)
  933. for fn in fns:
  934. try:
  935. names.extend(fn(connection, **name_kw))
  936. except NotImplementedError:
  937. pass
  938. if filter_names:
  939. filter_names = set(filter_names)
  940. # iterate over all the tables/views and call the single table method
  941. for table in names:
  942. if not filter_names or table in filter_names:
  943. key = (schema, table)
  944. try:
  945. yield (
  946. key,
  947. single_tbl_method(
  948. connection, table, schema=schema, **kw
  949. ),
  950. )
  951. except exc.UnreflectableTableError as err:
  952. if key not in unreflectable:
  953. unreflectable[key] = err
  954. except exc.NoSuchTableError:
  955. pass
  956. def get_multi_table_options(self, connection, **kw):
  957. return self._default_multi_reflect(
  958. self.get_table_options, connection, **kw
  959. )
  960. def get_multi_columns(self, connection, **kw):
  961. return self._default_multi_reflect(self.get_columns, connection, **kw)
  962. def get_multi_pk_constraint(self, connection, **kw):
  963. return self._default_multi_reflect(
  964. self.get_pk_constraint, connection, **kw
  965. )
  966. def get_multi_foreign_keys(self, connection, **kw):
  967. return self._default_multi_reflect(
  968. self.get_foreign_keys, connection, **kw
  969. )
  970. def get_multi_indexes(self, connection, **kw):
  971. return self._default_multi_reflect(self.get_indexes, connection, **kw)
  972. def get_multi_unique_constraints(self, connection, **kw):
  973. return self._default_multi_reflect(
  974. self.get_unique_constraints, connection, **kw
  975. )
  976. def get_multi_check_constraints(self, connection, **kw):
  977. return self._default_multi_reflect(
  978. self.get_check_constraints, connection, **kw
  979. )
  980. def get_multi_table_comment(self, connection, **kw):
  981. return self._default_multi_reflect(
  982. self.get_table_comment, connection, **kw
  983. )
  984. class StrCompileDialect(DefaultDialect):
  985. statement_compiler = compiler.StrSQLCompiler
  986. ddl_compiler = compiler.DDLCompiler
  987. type_compiler_cls = compiler.StrSQLTypeCompiler
  988. preparer = compiler.IdentifierPreparer
  989. insert_returning = True
  990. update_returning = True
  991. delete_returning = True
  992. supports_statement_cache = True
  993. supports_identity_columns = True
  994. supports_sequences = True
  995. sequences_optional = True
  996. preexecute_autoincrement_sequences = False
  997. supports_native_boolean = True
  998. supports_multivalues_insert = True
  999. supports_simple_order_by_label = True
  1000. class DefaultExecutionContext(ExecutionContext):
  1001. isinsert = False
  1002. isupdate = False
  1003. isdelete = False
  1004. is_crud = False
  1005. is_text = False
  1006. isddl = False
  1007. execute_style: ExecuteStyle = ExecuteStyle.EXECUTE
  1008. compiled: Optional[Compiled] = None
  1009. result_column_struct: Optional[
  1010. Tuple[List[ResultColumnsEntry], bool, bool, bool, bool]
  1011. ] = None
  1012. returned_default_rows: Optional[Sequence[Row[Any]]] = None
  1013. execution_options: _ExecuteOptions = util.EMPTY_DICT
  1014. cursor_fetch_strategy = _cursor._DEFAULT_FETCH
  1015. invoked_statement: Optional[Executable] = None
  1016. _is_implicit_returning = False
  1017. _is_explicit_returning = False
  1018. _is_supplemental_returning = False
  1019. _is_server_side = False
  1020. _soft_closed = False
  1021. _rowcount: Optional[int] = None
  1022. # a hook for SQLite's translation of
  1023. # result column names
  1024. # NOTE: pyhive is using this hook, can't remove it :(
  1025. _translate_colname: Optional[Callable[[str], str]] = None
  1026. _expanded_parameters: Mapping[str, List[str]] = util.immutabledict()
  1027. """used by set_input_sizes().
  1028. This collection comes from ``ExpandedState.parameter_expansion``.
  1029. """
  1030. cache_hit = NO_CACHE_KEY
  1031. root_connection: Connection
  1032. _dbapi_connection: PoolProxiedConnection
  1033. dialect: Dialect
  1034. unicode_statement: str
  1035. cursor: DBAPICursor
  1036. compiled_parameters: List[_MutableCoreSingleExecuteParams]
  1037. parameters: _DBAPIMultiExecuteParams
  1038. extracted_parameters: Optional[Sequence[BindParameter[Any]]]
  1039. _empty_dict_params = cast("Mapping[str, Any]", util.EMPTY_DICT)
  1040. _insertmanyvalues_rows: Optional[List[Tuple[Any, ...]]] = None
  1041. _num_sentinel_cols: int = 0
  1042. @classmethod
  1043. def _init_ddl(
  1044. cls,
  1045. dialect: Dialect,
  1046. connection: Connection,
  1047. dbapi_connection: PoolProxiedConnection,
  1048. execution_options: _ExecuteOptions,
  1049. compiled_ddl: DDLCompiler,
  1050. ) -> ExecutionContext:
  1051. """Initialize execution context for an ExecutableDDLElement
  1052. construct."""
  1053. self = cls.__new__(cls)
  1054. self.root_connection = connection
  1055. self._dbapi_connection = dbapi_connection
  1056. self.dialect = connection.dialect
  1057. self.compiled = compiled = compiled_ddl
  1058. self.isddl = True
  1059. self.execution_options = execution_options
  1060. self.unicode_statement = str(compiled)
  1061. if compiled.schema_translate_map:
  1062. schema_translate_map = self.execution_options.get(
  1063. "schema_translate_map", {}
  1064. )
  1065. rst = compiled.preparer._render_schema_translates
  1066. self.unicode_statement = rst(
  1067. self.unicode_statement, schema_translate_map
  1068. )
  1069. self.statement = self.unicode_statement
  1070. self.cursor = self.create_cursor()
  1071. self.compiled_parameters = []
  1072. if dialect.positional:
  1073. self.parameters = [dialect.execute_sequence_format()]
  1074. else:
  1075. self.parameters = [self._empty_dict_params]
  1076. return self
  1077. @classmethod
  1078. def _init_compiled(
  1079. cls,
  1080. dialect: Dialect,
  1081. connection: Connection,
  1082. dbapi_connection: PoolProxiedConnection,
  1083. execution_options: _ExecuteOptions,
  1084. compiled: SQLCompiler,
  1085. parameters: _CoreMultiExecuteParams,
  1086. invoked_statement: Executable,
  1087. extracted_parameters: Optional[Sequence[BindParameter[Any]]],
  1088. cache_hit: CacheStats = CacheStats.CACHING_DISABLED,
  1089. ) -> ExecutionContext:
  1090. """Initialize execution context for a Compiled construct."""
  1091. self = cls.__new__(cls)
  1092. self.root_connection = connection
  1093. self._dbapi_connection = dbapi_connection
  1094. self.dialect = connection.dialect
  1095. self.extracted_parameters = extracted_parameters
  1096. self.invoked_statement = invoked_statement
  1097. self.compiled = compiled
  1098. self.cache_hit = cache_hit
  1099. self.execution_options = execution_options
  1100. self.result_column_struct = (
  1101. compiled._result_columns,
  1102. compiled._ordered_columns,
  1103. compiled._textual_ordered_columns,
  1104. compiled._ad_hoc_textual,
  1105. compiled._loose_column_name_matching,
  1106. )
  1107. self.isinsert = ii = compiled.isinsert
  1108. self.isupdate = iu = compiled.isupdate
  1109. self.isdelete = id_ = compiled.isdelete
  1110. self.is_text = compiled.isplaintext
  1111. if ii or iu or id_:
  1112. dml_statement = compiled.compile_state.statement # type: ignore
  1113. if TYPE_CHECKING:
  1114. assert isinstance(dml_statement, UpdateBase)
  1115. self.is_crud = True
  1116. self._is_explicit_returning = ier = bool(dml_statement._returning)
  1117. self._is_implicit_returning = iir = bool(
  1118. compiled.implicit_returning
  1119. )
  1120. if iir and dml_statement._supplemental_returning:
  1121. self._is_supplemental_returning = True
  1122. # dont mix implicit and explicit returning
  1123. assert not (iir and ier)
  1124. if (ier or iir) and compiled.for_executemany:
  1125. if ii and not self.dialect.insert_executemany_returning:
  1126. raise exc.InvalidRequestError(
  1127. f"Dialect {self.dialect.dialect_description} with "
  1128. f"current server capabilities does not support "
  1129. "INSERT..RETURNING when executemany is used"
  1130. )
  1131. elif (
  1132. ii
  1133. and dml_statement._sort_by_parameter_order
  1134. and not self.dialect.insert_executemany_returning_sort_by_parameter_order # noqa: E501
  1135. ):
  1136. raise exc.InvalidRequestError(
  1137. f"Dialect {self.dialect.dialect_description} with "
  1138. f"current server capabilities does not support "
  1139. "INSERT..RETURNING with deterministic row ordering "
  1140. "when executemany is used"
  1141. )
  1142. elif (
  1143. ii
  1144. and self.dialect.use_insertmanyvalues
  1145. and not compiled._insertmanyvalues
  1146. ):
  1147. raise exc.InvalidRequestError(
  1148. 'Statement does not have "insertmanyvalues" '
  1149. "enabled, can't use INSERT..RETURNING with "
  1150. "executemany in this case."
  1151. )
  1152. elif iu and not self.dialect.update_executemany_returning:
  1153. raise exc.InvalidRequestError(
  1154. f"Dialect {self.dialect.dialect_description} with "
  1155. f"current server capabilities does not support "
  1156. "UPDATE..RETURNING when executemany is used"
  1157. )
  1158. elif id_ and not self.dialect.delete_executemany_returning:
  1159. raise exc.InvalidRequestError(
  1160. f"Dialect {self.dialect.dialect_description} with "
  1161. f"current server capabilities does not support "
  1162. "DELETE..RETURNING when executemany is used"
  1163. )
  1164. if not parameters:
  1165. self.compiled_parameters = [
  1166. compiled.construct_params(
  1167. extracted_parameters=extracted_parameters,
  1168. escape_names=False,
  1169. )
  1170. ]
  1171. else:
  1172. self.compiled_parameters = [
  1173. compiled.construct_params(
  1174. m,
  1175. escape_names=False,
  1176. _group_number=grp,
  1177. extracted_parameters=extracted_parameters,
  1178. )
  1179. for grp, m in enumerate(parameters)
  1180. ]
  1181. if len(parameters) > 1:
  1182. if self.isinsert and compiled._insertmanyvalues:
  1183. self.execute_style = ExecuteStyle.INSERTMANYVALUES
  1184. imv = compiled._insertmanyvalues
  1185. if imv.sentinel_columns is not None:
  1186. self._num_sentinel_cols = imv.num_sentinel_columns
  1187. else:
  1188. self.execute_style = ExecuteStyle.EXECUTEMANY
  1189. self.unicode_statement = compiled.string
  1190. self.cursor = self.create_cursor()
  1191. if self.compiled.insert_prefetch or self.compiled.update_prefetch:
  1192. self._process_execute_defaults()
  1193. processors = compiled._bind_processors
  1194. flattened_processors: Mapping[
  1195. str, _BindProcessorType[Any]
  1196. ] = processors # type: ignore[assignment]
  1197. if compiled.literal_execute_params or compiled.post_compile_params:
  1198. if self.executemany:
  1199. raise exc.InvalidRequestError(
  1200. "'literal_execute' or 'expanding' parameters can't be "
  1201. "used with executemany()"
  1202. )
  1203. expanded_state = compiled._process_parameters_for_postcompile(
  1204. self.compiled_parameters[0]
  1205. )
  1206. # re-assign self.unicode_statement
  1207. self.unicode_statement = expanded_state.statement
  1208. self._expanded_parameters = expanded_state.parameter_expansion
  1209. flattened_processors = dict(processors) # type: ignore
  1210. flattened_processors.update(expanded_state.processors)
  1211. positiontup = expanded_state.positiontup
  1212. elif compiled.positional:
  1213. positiontup = self.compiled.positiontup
  1214. else:
  1215. positiontup = None
  1216. if compiled.schema_translate_map:
  1217. schema_translate_map = self.execution_options.get(
  1218. "schema_translate_map", {}
  1219. )
  1220. rst = compiled.preparer._render_schema_translates
  1221. self.unicode_statement = rst(
  1222. self.unicode_statement, schema_translate_map
  1223. )
  1224. # final self.unicode_statement is now assigned, encode if needed
  1225. # by dialect
  1226. self.statement = self.unicode_statement
  1227. # Convert the dictionary of bind parameter values
  1228. # into a dict or list to be sent to the DBAPI's
  1229. # execute() or executemany() method.
  1230. if compiled.positional:
  1231. core_positional_parameters: MutableSequence[Sequence[Any]] = []
  1232. assert positiontup is not None
  1233. for compiled_params in self.compiled_parameters:
  1234. l_param: List[Any] = [
  1235. (
  1236. flattened_processors[key](compiled_params[key])
  1237. if key in flattened_processors
  1238. else compiled_params[key]
  1239. )
  1240. for key in positiontup
  1241. ]
  1242. core_positional_parameters.append(
  1243. dialect.execute_sequence_format(l_param)
  1244. )
  1245. self.parameters = core_positional_parameters
  1246. else:
  1247. core_dict_parameters: MutableSequence[Dict[str, Any]] = []
  1248. escaped_names = compiled.escaped_bind_names
  1249. # note that currently, "expanded" parameters will be present
  1250. # in self.compiled_parameters in their quoted form. This is
  1251. # slightly inconsistent with the approach taken as of
  1252. # #8056 where self.compiled_parameters is meant to contain unquoted
  1253. # param names.
  1254. d_param: Dict[str, Any]
  1255. for compiled_params in self.compiled_parameters:
  1256. if escaped_names:
  1257. d_param = {
  1258. escaped_names.get(key, key): (
  1259. flattened_processors[key](compiled_params[key])
  1260. if key in flattened_processors
  1261. else compiled_params[key]
  1262. )
  1263. for key in compiled_params
  1264. }
  1265. else:
  1266. d_param = {
  1267. key: (
  1268. flattened_processors[key](compiled_params[key])
  1269. if key in flattened_processors
  1270. else compiled_params[key]
  1271. )
  1272. for key in compiled_params
  1273. }
  1274. core_dict_parameters.append(d_param)
  1275. self.parameters = core_dict_parameters
  1276. return self
  1277. @classmethod
  1278. def _init_statement(
  1279. cls,
  1280. dialect: Dialect,
  1281. connection: Connection,
  1282. dbapi_connection: PoolProxiedConnection,
  1283. execution_options: _ExecuteOptions,
  1284. statement: str,
  1285. parameters: _DBAPIMultiExecuteParams,
  1286. ) -> ExecutionContext:
  1287. """Initialize execution context for a string SQL statement."""
  1288. self = cls.__new__(cls)
  1289. self.root_connection = connection
  1290. self._dbapi_connection = dbapi_connection
  1291. self.dialect = connection.dialect
  1292. self.is_text = True
  1293. self.execution_options = execution_options
  1294. if not parameters:
  1295. if self.dialect.positional:
  1296. self.parameters = [dialect.execute_sequence_format()]
  1297. else:
  1298. self.parameters = [self._empty_dict_params]
  1299. elif isinstance(parameters[0], dialect.execute_sequence_format):
  1300. self.parameters = parameters
  1301. elif isinstance(parameters[0], dict):
  1302. self.parameters = parameters
  1303. else:
  1304. self.parameters = [
  1305. dialect.execute_sequence_format(p) for p in parameters
  1306. ]
  1307. if len(parameters) > 1:
  1308. self.execute_style = ExecuteStyle.EXECUTEMANY
  1309. self.statement = self.unicode_statement = statement
  1310. self.cursor = self.create_cursor()
  1311. return self
  1312. @classmethod
  1313. def _init_default(
  1314. cls,
  1315. dialect: Dialect,
  1316. connection: Connection,
  1317. dbapi_connection: PoolProxiedConnection,
  1318. execution_options: _ExecuteOptions,
  1319. ) -> ExecutionContext:
  1320. """Initialize execution context for a ColumnDefault construct."""
  1321. self = cls.__new__(cls)
  1322. self.root_connection = connection
  1323. self._dbapi_connection = dbapi_connection
  1324. self.dialect = connection.dialect
  1325. self.execution_options = execution_options
  1326. self.cursor = self.create_cursor()
  1327. return self
  1328. def _get_cache_stats(self) -> str:
  1329. if self.compiled is None:
  1330. return "raw sql"
  1331. now = perf_counter()
  1332. ch = self.cache_hit
  1333. gen_time = self.compiled._gen_time
  1334. assert gen_time is not None
  1335. if ch is NO_CACHE_KEY:
  1336. return "no key %.5fs" % (now - gen_time,)
  1337. elif ch is CACHE_HIT:
  1338. return "cached since %.4gs ago" % (now - gen_time,)
  1339. elif ch is CACHE_MISS:
  1340. return "generated in %.5fs" % (now - gen_time,)
  1341. elif ch is CACHING_DISABLED:
  1342. if "_cache_disable_reason" in self.execution_options:
  1343. return "caching disabled (%s) %.5fs " % (
  1344. self.execution_options["_cache_disable_reason"],
  1345. now - gen_time,
  1346. )
  1347. else:
  1348. return "caching disabled %.5fs" % (now - gen_time,)
  1349. elif ch is NO_DIALECT_SUPPORT:
  1350. return "dialect %s+%s does not support caching %.5fs" % (
  1351. self.dialect.name,
  1352. self.dialect.driver,
  1353. now - gen_time,
  1354. )
  1355. else:
  1356. return "unknown"
  1357. @property
  1358. def executemany(self): # type: ignore[override]
  1359. return self.execute_style in (
  1360. ExecuteStyle.EXECUTEMANY,
  1361. ExecuteStyle.INSERTMANYVALUES,
  1362. )
  1363. @util.memoized_property
  1364. def identifier_preparer(self):
  1365. if self.compiled:
  1366. return self.compiled.preparer
  1367. elif "schema_translate_map" in self.execution_options:
  1368. return self.dialect.identifier_preparer._with_schema_translate(
  1369. self.execution_options["schema_translate_map"]
  1370. )
  1371. else:
  1372. return self.dialect.identifier_preparer
  1373. @util.memoized_property
  1374. def engine(self):
  1375. return self.root_connection.engine
  1376. @util.memoized_property
  1377. def postfetch_cols(self) -> Optional[Sequence[Column[Any]]]:
  1378. if TYPE_CHECKING:
  1379. assert isinstance(self.compiled, SQLCompiler)
  1380. return self.compiled.postfetch
  1381. @util.memoized_property
  1382. def prefetch_cols(self) -> Optional[Sequence[Column[Any]]]:
  1383. if TYPE_CHECKING:
  1384. assert isinstance(self.compiled, SQLCompiler)
  1385. if self.isinsert:
  1386. return self.compiled.insert_prefetch
  1387. elif self.isupdate:
  1388. return self.compiled.update_prefetch
  1389. else:
  1390. return ()
  1391. @util.memoized_property
  1392. def no_parameters(self):
  1393. return self.execution_options.get("no_parameters", False)
  1394. def _execute_scalar(
  1395. self,
  1396. stmt: str,
  1397. type_: Optional[TypeEngine[Any]],
  1398. parameters: Optional[_DBAPISingleExecuteParams] = None,
  1399. ) -> Any:
  1400. """Execute a string statement on the current cursor, returning a
  1401. scalar result.
  1402. Used to fire off sequences, default phrases, and "select lastrowid"
  1403. types of statements individually or in the context of a parent INSERT
  1404. or UPDATE statement.
  1405. """
  1406. conn = self.root_connection
  1407. if "schema_translate_map" in self.execution_options:
  1408. schema_translate_map = self.execution_options.get(
  1409. "schema_translate_map", {}
  1410. )
  1411. rst = self.identifier_preparer._render_schema_translates
  1412. stmt = rst(stmt, schema_translate_map)
  1413. if not parameters:
  1414. if self.dialect.positional:
  1415. parameters = self.dialect.execute_sequence_format()
  1416. else:
  1417. parameters = {}
  1418. conn._cursor_execute(self.cursor, stmt, parameters, context=self)
  1419. row = self.cursor.fetchone()
  1420. if row is not None:
  1421. r = row[0]
  1422. else:
  1423. r = None
  1424. if type_ is not None:
  1425. # apply type post processors to the result
  1426. proc = type_._cached_result_processor(
  1427. self.dialect, self.cursor.description[0][1]
  1428. )
  1429. if proc:
  1430. return proc(r)
  1431. return r
  1432. @util.memoized_property
  1433. def connection(self):
  1434. return self.root_connection
  1435. def _use_server_side_cursor(self):
  1436. if not self.dialect.supports_server_side_cursors:
  1437. return False
  1438. if self.dialect.server_side_cursors:
  1439. # this is deprecated
  1440. use_server_side = self.execution_options.get(
  1441. "stream_results", True
  1442. ) and (
  1443. self.compiled
  1444. and isinstance(self.compiled.statement, expression.Selectable)
  1445. or (
  1446. (
  1447. not self.compiled
  1448. or isinstance(
  1449. self.compiled.statement, expression.TextClause
  1450. )
  1451. )
  1452. and self.unicode_statement
  1453. and SERVER_SIDE_CURSOR_RE.match(self.unicode_statement)
  1454. )
  1455. )
  1456. else:
  1457. use_server_side = self.execution_options.get(
  1458. "stream_results", False
  1459. )
  1460. return use_server_side
  1461. def create_cursor(self) -> DBAPICursor:
  1462. if (
  1463. # inlining initial preference checks for SS cursors
  1464. self.dialect.supports_server_side_cursors
  1465. and (
  1466. self.execution_options.get("stream_results", False)
  1467. or (
  1468. self.dialect.server_side_cursors
  1469. and self._use_server_side_cursor()
  1470. )
  1471. )
  1472. ):
  1473. self._is_server_side = True
  1474. return self.create_server_side_cursor()
  1475. else:
  1476. self._is_server_side = False
  1477. return self.create_default_cursor()
  1478. def fetchall_for_returning(self, cursor):
  1479. return cursor.fetchall()
  1480. def create_default_cursor(self) -> DBAPICursor:
  1481. return self._dbapi_connection.cursor()
  1482. def create_server_side_cursor(self) -> DBAPICursor:
  1483. raise NotImplementedError()
  1484. def pre_exec(self):
  1485. pass
  1486. def get_out_parameter_values(self, names):
  1487. raise NotImplementedError(
  1488. "This dialect does not support OUT parameters"
  1489. )
  1490. def post_exec(self):
  1491. pass
  1492. def get_result_processor(self, type_, colname, coltype):
  1493. """Return a 'result processor' for a given type as present in
  1494. cursor.description.
  1495. This has a default implementation that dialects can override
  1496. for context-sensitive result type handling.
  1497. """
  1498. return type_._cached_result_processor(self.dialect, coltype)
  1499. def get_lastrowid(self):
  1500. """return self.cursor.lastrowid, or equivalent, after an INSERT.
  1501. This may involve calling special cursor functions, issuing a new SELECT
  1502. on the cursor (or a new one), or returning a stored value that was
  1503. calculated within post_exec().
  1504. This function will only be called for dialects which support "implicit"
  1505. primary key generation, keep preexecute_autoincrement_sequences set to
  1506. False, and when no explicit id value was bound to the statement.
  1507. The function is called once for an INSERT statement that would need to
  1508. return the last inserted primary key for those dialects that make use
  1509. of the lastrowid concept. In these cases, it is called directly after
  1510. :meth:`.ExecutionContext.post_exec`.
  1511. """
  1512. return self.cursor.lastrowid
  1513. def handle_dbapi_exception(self, e):
  1514. pass
  1515. @util.non_memoized_property
  1516. def rowcount(self) -> int:
  1517. if self._rowcount is not None:
  1518. return self._rowcount
  1519. else:
  1520. return self.cursor.rowcount
  1521. @property
  1522. def _has_rowcount(self):
  1523. return self._rowcount is not None
  1524. def supports_sane_rowcount(self):
  1525. return self.dialect.supports_sane_rowcount
  1526. def supports_sane_multi_rowcount(self):
  1527. return self.dialect.supports_sane_multi_rowcount
  1528. def _setup_result_proxy(self):
  1529. exec_opt = self.execution_options
  1530. if self._rowcount is None and exec_opt.get("preserve_rowcount", False):
  1531. self._rowcount = self.cursor.rowcount
  1532. yp: Optional[Union[int, bool]]
  1533. if self.is_crud or self.is_text:
  1534. result = self._setup_dml_or_text_result()
  1535. yp = False
  1536. else:
  1537. yp = exec_opt.get("yield_per", None)
  1538. sr = self._is_server_side or exec_opt.get("stream_results", False)
  1539. strategy = self.cursor_fetch_strategy
  1540. if sr and strategy is _cursor._DEFAULT_FETCH:
  1541. strategy = _cursor.BufferedRowCursorFetchStrategy(
  1542. self.cursor, self.execution_options
  1543. )
  1544. cursor_description: _DBAPICursorDescription = (
  1545. strategy.alternate_cursor_description
  1546. or self.cursor.description
  1547. )
  1548. if cursor_description is None:
  1549. strategy = _cursor._NO_CURSOR_DQL
  1550. result = _cursor.CursorResult(self, strategy, cursor_description)
  1551. compiled = self.compiled
  1552. if (
  1553. compiled
  1554. and not self.isddl
  1555. and cast(SQLCompiler, compiled).has_out_parameters
  1556. ):
  1557. self._setup_out_parameters(result)
  1558. self._soft_closed = result._soft_closed
  1559. if yp:
  1560. result = result.yield_per(yp)
  1561. return result
  1562. def _setup_out_parameters(self, result):
  1563. compiled = cast(SQLCompiler, self.compiled)
  1564. out_bindparams = [
  1565. (param, name)
  1566. for param, name in compiled.bind_names.items()
  1567. if param.isoutparam
  1568. ]
  1569. out_parameters = {}
  1570. for bindparam, raw_value in zip(
  1571. [param for param, name in out_bindparams],
  1572. self.get_out_parameter_values(
  1573. [name for param, name in out_bindparams]
  1574. ),
  1575. ):
  1576. type_ = bindparam.type
  1577. impl_type = type_.dialect_impl(self.dialect)
  1578. dbapi_type = impl_type.get_dbapi_type(self.dialect.loaded_dbapi)
  1579. result_processor = impl_type.result_processor(
  1580. self.dialect, dbapi_type
  1581. )
  1582. if result_processor is not None:
  1583. raw_value = result_processor(raw_value)
  1584. out_parameters[bindparam.key] = raw_value
  1585. result.out_parameters = out_parameters
  1586. def _setup_dml_or_text_result(self):
  1587. compiled = cast(SQLCompiler, self.compiled)
  1588. strategy: ResultFetchStrategy = self.cursor_fetch_strategy
  1589. if self.isinsert:
  1590. if (
  1591. self.execute_style is ExecuteStyle.INSERTMANYVALUES
  1592. and compiled.effective_returning
  1593. ):
  1594. strategy = _cursor.FullyBufferedCursorFetchStrategy(
  1595. self.cursor,
  1596. initial_buffer=self._insertmanyvalues_rows,
  1597. # maintain alt cursor description if set by the
  1598. # dialect, e.g. mssql preserves it
  1599. alternate_description=(
  1600. strategy.alternate_cursor_description
  1601. ),
  1602. )
  1603. if compiled.postfetch_lastrowid:
  1604. self.inserted_primary_key_rows = (
  1605. self._setup_ins_pk_from_lastrowid()
  1606. )
  1607. # else if not self._is_implicit_returning,
  1608. # the default inserted_primary_key_rows accessor will
  1609. # return an "empty" primary key collection when accessed.
  1610. if self._is_server_side and strategy is _cursor._DEFAULT_FETCH:
  1611. strategy = _cursor.BufferedRowCursorFetchStrategy(
  1612. self.cursor, self.execution_options
  1613. )
  1614. if strategy is _cursor._NO_CURSOR_DML:
  1615. cursor_description = None
  1616. else:
  1617. cursor_description = (
  1618. strategy.alternate_cursor_description
  1619. or self.cursor.description
  1620. )
  1621. if cursor_description is None:
  1622. strategy = _cursor._NO_CURSOR_DML
  1623. elif self._num_sentinel_cols:
  1624. assert self.execute_style is ExecuteStyle.INSERTMANYVALUES
  1625. # strip out the sentinel columns from cursor description
  1626. # a similar logic is done to the rows only in CursorResult
  1627. cursor_description = cursor_description[
  1628. 0 : -self._num_sentinel_cols
  1629. ]
  1630. result: _cursor.CursorResult[Any] = _cursor.CursorResult(
  1631. self, strategy, cursor_description
  1632. )
  1633. if self.isinsert:
  1634. if self._is_implicit_returning:
  1635. rows = result.all()
  1636. self.returned_default_rows = rows
  1637. self.inserted_primary_key_rows = (
  1638. self._setup_ins_pk_from_implicit_returning(result, rows)
  1639. )
  1640. # test that it has a cursor metadata that is accurate. the
  1641. # first row will have been fetched and current assumptions
  1642. # are that the result has only one row, until executemany()
  1643. # support is added here.
  1644. assert result._metadata.returns_rows
  1645. # Insert statement has both return_defaults() and
  1646. # returning(). rewind the result on the list of rows
  1647. # we just used.
  1648. if self._is_supplemental_returning:
  1649. result._rewind(rows)
  1650. else:
  1651. result._soft_close()
  1652. elif not self._is_explicit_returning:
  1653. result._soft_close()
  1654. # we assume here the result does not return any rows.
  1655. # *usually*, this will be true. However, some dialects
  1656. # such as that of MSSQL/pyodbc need to SELECT a post fetch
  1657. # function so this is not necessarily true.
  1658. # assert not result.returns_rows
  1659. elif self._is_implicit_returning:
  1660. rows = result.all()
  1661. if rows:
  1662. self.returned_default_rows = rows
  1663. self._rowcount = len(rows)
  1664. if self._is_supplemental_returning:
  1665. result._rewind(rows)
  1666. else:
  1667. result._soft_close()
  1668. # test that it has a cursor metadata that is accurate.
  1669. # the rows have all been fetched however.
  1670. assert result._metadata.returns_rows
  1671. elif not result._metadata.returns_rows:
  1672. # no results, get rowcount
  1673. # (which requires open cursor on some drivers)
  1674. if self._rowcount is None:
  1675. self._rowcount = self.cursor.rowcount
  1676. result._soft_close()
  1677. elif self.isupdate or self.isdelete:
  1678. if self._rowcount is None:
  1679. self._rowcount = self.cursor.rowcount
  1680. return result
  1681. @util.memoized_property
  1682. def inserted_primary_key_rows(self):
  1683. # if no specific "get primary key" strategy was set up
  1684. # during execution, return a "default" primary key based
  1685. # on what's in the compiled_parameters and nothing else.
  1686. return self._setup_ins_pk_from_empty()
  1687. def _setup_ins_pk_from_lastrowid(self):
  1688. getter = cast(
  1689. SQLCompiler, self.compiled
  1690. )._inserted_primary_key_from_lastrowid_getter
  1691. lastrowid = self.get_lastrowid()
  1692. return [getter(lastrowid, self.compiled_parameters[0])]
  1693. def _setup_ins_pk_from_empty(self):
  1694. getter = cast(
  1695. SQLCompiler, self.compiled
  1696. )._inserted_primary_key_from_lastrowid_getter
  1697. return [getter(None, param) for param in self.compiled_parameters]
  1698. def _setup_ins_pk_from_implicit_returning(self, result, rows):
  1699. if not rows:
  1700. return []
  1701. getter = cast(
  1702. SQLCompiler, self.compiled
  1703. )._inserted_primary_key_from_returning_getter
  1704. compiled_params = self.compiled_parameters
  1705. return [
  1706. getter(row, param) for row, param in zip(rows, compiled_params)
  1707. ]
  1708. def lastrow_has_defaults(self):
  1709. return (self.isinsert or self.isupdate) and bool(
  1710. cast(SQLCompiler, self.compiled).postfetch
  1711. )
  1712. def _prepare_set_input_sizes(
  1713. self,
  1714. ) -> Optional[List[Tuple[str, Any, TypeEngine[Any]]]]:
  1715. """Given a cursor and ClauseParameters, prepare arguments
  1716. in order to call the appropriate
  1717. style of ``setinputsizes()`` on the cursor, using DB-API types
  1718. from the bind parameter's ``TypeEngine`` objects.
  1719. This method only called by those dialects which set the
  1720. :attr:`.Dialect.bind_typing` attribute to
  1721. :attr:`.BindTyping.SETINPUTSIZES`. Python-oracledb and cx_Oracle are
  1722. the only DBAPIs that requires setinputsizes(); pyodbc offers it as an
  1723. option.
  1724. Prior to SQLAlchemy 2.0, the setinputsizes() approach was also used
  1725. for pg8000 and asyncpg, which has been changed to inline rendering
  1726. of casts.
  1727. """
  1728. if self.isddl or self.is_text:
  1729. return None
  1730. compiled = cast(SQLCompiler, self.compiled)
  1731. inputsizes = compiled._get_set_input_sizes_lookup()
  1732. if inputsizes is None:
  1733. return None
  1734. dialect = self.dialect
  1735. # all of the rest of this... cython?
  1736. if dialect._has_events:
  1737. inputsizes = dict(inputsizes)
  1738. dialect.dispatch.do_setinputsizes(
  1739. inputsizes, self.cursor, self.statement, self.parameters, self
  1740. )
  1741. if compiled.escaped_bind_names:
  1742. escaped_bind_names = compiled.escaped_bind_names
  1743. else:
  1744. escaped_bind_names = None
  1745. if dialect.positional:
  1746. items = [
  1747. (key, compiled.binds[key])
  1748. for key in compiled.positiontup or ()
  1749. ]
  1750. else:
  1751. items = [
  1752. (key, bindparam)
  1753. for bindparam, key in compiled.bind_names.items()
  1754. ]
  1755. generic_inputsizes: List[Tuple[str, Any, TypeEngine[Any]]] = []
  1756. for key, bindparam in items:
  1757. if bindparam in compiled.literal_execute_params:
  1758. continue
  1759. if key in self._expanded_parameters:
  1760. if is_tuple_type(bindparam.type):
  1761. num = len(bindparam.type.types)
  1762. dbtypes = inputsizes[bindparam]
  1763. generic_inputsizes.extend(
  1764. (
  1765. (
  1766. escaped_bind_names.get(paramname, paramname)
  1767. if escaped_bind_names is not None
  1768. else paramname
  1769. ),
  1770. dbtypes[idx % num],
  1771. bindparam.type.types[idx % num],
  1772. )
  1773. for idx, paramname in enumerate(
  1774. self._expanded_parameters[key]
  1775. )
  1776. )
  1777. else:
  1778. dbtype = inputsizes.get(bindparam, None)
  1779. generic_inputsizes.extend(
  1780. (
  1781. (
  1782. escaped_bind_names.get(paramname, paramname)
  1783. if escaped_bind_names is not None
  1784. else paramname
  1785. ),
  1786. dbtype,
  1787. bindparam.type,
  1788. )
  1789. for paramname in self._expanded_parameters[key]
  1790. )
  1791. else:
  1792. dbtype = inputsizes.get(bindparam, None)
  1793. escaped_name = (
  1794. escaped_bind_names.get(key, key)
  1795. if escaped_bind_names is not None
  1796. else key
  1797. )
  1798. generic_inputsizes.append(
  1799. (escaped_name, dbtype, bindparam.type)
  1800. )
  1801. return generic_inputsizes
  1802. def _exec_default(self, column, default, type_):
  1803. if default.is_sequence:
  1804. return self.fire_sequence(default, type_)
  1805. elif default.is_callable:
  1806. # this codepath is not normally used as it's inlined
  1807. # into _process_execute_defaults
  1808. self.current_column = column
  1809. return default.arg(self)
  1810. elif default.is_clause_element:
  1811. return self._exec_default_clause_element(column, default, type_)
  1812. else:
  1813. # this codepath is not normally used as it's inlined
  1814. # into _process_execute_defaults
  1815. return default.arg
  1816. def _exec_default_clause_element(self, column, default, type_):
  1817. # execute a default that's a complete clause element. Here, we have
  1818. # to re-implement a miniature version of the compile->parameters->
  1819. # cursor.execute() sequence, since we don't want to modify the state
  1820. # of the connection / result in progress or create new connection/
  1821. # result objects etc.
  1822. # .. versionchanged:: 1.4
  1823. if not default._arg_is_typed:
  1824. default_arg = expression.type_coerce(default.arg, type_)
  1825. else:
  1826. default_arg = default.arg
  1827. compiled = expression.select(default_arg).compile(dialect=self.dialect)
  1828. compiled_params = compiled.construct_params()
  1829. processors = compiled._bind_processors
  1830. if compiled.positional:
  1831. parameters = self.dialect.execute_sequence_format(
  1832. [
  1833. (
  1834. processors[key](compiled_params[key]) # type: ignore
  1835. if key in processors
  1836. else compiled_params[key]
  1837. )
  1838. for key in compiled.positiontup or ()
  1839. ]
  1840. )
  1841. else:
  1842. parameters = {
  1843. key: (
  1844. processors[key](compiled_params[key]) # type: ignore
  1845. if key in processors
  1846. else compiled_params[key]
  1847. )
  1848. for key in compiled_params
  1849. }
  1850. return self._execute_scalar(
  1851. str(compiled), type_, parameters=parameters
  1852. )
  1853. current_parameters: Optional[_CoreSingleExecuteParams] = None
  1854. """A dictionary of parameters applied to the current row.
  1855. This attribute is only available in the context of a user-defined default
  1856. generation function, e.g. as described at :ref:`context_default_functions`.
  1857. It consists of a dictionary which includes entries for each column/value
  1858. pair that is to be part of the INSERT or UPDATE statement. The keys of the
  1859. dictionary will be the key value of each :class:`_schema.Column`,
  1860. which is usually
  1861. synonymous with the name.
  1862. Note that the :attr:`.DefaultExecutionContext.current_parameters` attribute
  1863. does not accommodate for the "multi-values" feature of the
  1864. :meth:`_expression.Insert.values` method. The
  1865. :meth:`.DefaultExecutionContext.get_current_parameters` method should be
  1866. preferred.
  1867. .. seealso::
  1868. :meth:`.DefaultExecutionContext.get_current_parameters`
  1869. :ref:`context_default_functions`
  1870. """
  1871. def get_current_parameters(self, isolate_multiinsert_groups=True):
  1872. """Return a dictionary of parameters applied to the current row.
  1873. This method can only be used in the context of a user-defined default
  1874. generation function, e.g. as described at
  1875. :ref:`context_default_functions`. When invoked, a dictionary is
  1876. returned which includes entries for each column/value pair that is part
  1877. of the INSERT or UPDATE statement. The keys of the dictionary will be
  1878. the key value of each :class:`_schema.Column`,
  1879. which is usually synonymous
  1880. with the name.
  1881. :param isolate_multiinsert_groups=True: indicates that multi-valued
  1882. INSERT constructs created using :meth:`_expression.Insert.values`
  1883. should be
  1884. handled by returning only the subset of parameters that are local
  1885. to the current column default invocation. When ``False``, the
  1886. raw parameters of the statement are returned including the
  1887. naming convention used in the case of multi-valued INSERT.
  1888. .. versionadded:: 1.2 added
  1889. :meth:`.DefaultExecutionContext.get_current_parameters`
  1890. which provides more functionality over the existing
  1891. :attr:`.DefaultExecutionContext.current_parameters`
  1892. attribute.
  1893. .. seealso::
  1894. :attr:`.DefaultExecutionContext.current_parameters`
  1895. :ref:`context_default_functions`
  1896. """
  1897. try:
  1898. parameters = self.current_parameters
  1899. column = self.current_column
  1900. except AttributeError:
  1901. raise exc.InvalidRequestError(
  1902. "get_current_parameters() can only be invoked in the "
  1903. "context of a Python side column default function"
  1904. )
  1905. else:
  1906. assert column is not None
  1907. assert parameters is not None
  1908. compile_state = cast(
  1909. "DMLState", cast(SQLCompiler, self.compiled).compile_state
  1910. )
  1911. assert compile_state is not None
  1912. if (
  1913. isolate_multiinsert_groups
  1914. and dml.isinsert(compile_state)
  1915. and compile_state._has_multi_parameters
  1916. ):
  1917. if column._is_multiparam_column:
  1918. index = column.index + 1
  1919. d = {column.original.key: parameters[column.key]}
  1920. else:
  1921. d = {column.key: parameters[column.key]}
  1922. index = 0
  1923. assert compile_state._dict_parameters is not None
  1924. keys = compile_state._dict_parameters.keys()
  1925. d.update(
  1926. (key, parameters["%s_m%d" % (key, index)]) for key in keys
  1927. )
  1928. return d
  1929. else:
  1930. return parameters
  1931. def get_insert_default(self, column):
  1932. if column.default is None:
  1933. return None
  1934. else:
  1935. return self._exec_default(column, column.default, column.type)
  1936. def get_update_default(self, column):
  1937. if column.onupdate is None:
  1938. return None
  1939. else:
  1940. return self._exec_default(column, column.onupdate, column.type)
  1941. def _process_execute_defaults(self):
  1942. compiled = cast(SQLCompiler, self.compiled)
  1943. key_getter = compiled._within_exec_param_key_getter
  1944. sentinel_counter = 0
  1945. if compiled.insert_prefetch:
  1946. prefetch_recs = [
  1947. (
  1948. c,
  1949. key_getter(c),
  1950. c._default_description_tuple,
  1951. self.get_insert_default,
  1952. )
  1953. for c in compiled.insert_prefetch
  1954. ]
  1955. elif compiled.update_prefetch:
  1956. prefetch_recs = [
  1957. (
  1958. c,
  1959. key_getter(c),
  1960. c._onupdate_description_tuple,
  1961. self.get_update_default,
  1962. )
  1963. for c in compiled.update_prefetch
  1964. ]
  1965. else:
  1966. prefetch_recs = []
  1967. for param in self.compiled_parameters:
  1968. self.current_parameters = param
  1969. for (
  1970. c,
  1971. param_key,
  1972. (arg, is_scalar, is_callable, is_sentinel),
  1973. fallback,
  1974. ) in prefetch_recs:
  1975. if is_sentinel:
  1976. param[param_key] = sentinel_counter
  1977. sentinel_counter += 1
  1978. elif is_scalar:
  1979. param[param_key] = arg
  1980. elif is_callable:
  1981. self.current_column = c
  1982. param[param_key] = arg(self)
  1983. else:
  1984. val = fallback(c)
  1985. if val is not None:
  1986. param[param_key] = val
  1987. del self.current_parameters
  1988. DefaultDialect.execution_ctx_cls = DefaultExecutionContext