Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py: 46%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1136 statements  

1# engine/default.py 

2# Copyright (C) 2005-2026 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 

9"""Default implementations of per-dialect sqlalchemy.engine classes. 

10 

11These are semi-private implementation classes which are only of importance 

12to database dialect authors; dialects will usually use the classes here 

13as the base class for their own corresponding classes. 

14 

15""" 

16 

17from __future__ import annotations 

18 

19import functools 

20import operator 

21import random 

22import re 

23from time import perf_counter 

24import typing 

25from typing import Any 

26from typing import Callable 

27from typing import cast 

28from typing import Dict 

29from typing import Final 

30from typing import Iterable 

31from typing import List 

32from typing import Literal 

33from typing import Mapping 

34from typing import MutableMapping 

35from typing import MutableSequence 

36from typing import Optional 

37from typing import Sequence 

38from typing import Set 

39from typing import Tuple 

40from typing import Type 

41from typing import TYPE_CHECKING 

42from typing import Union 

43import weakref 

44 

45from . import characteristics 

46from . import cursor as _cursor 

47from . import interfaces 

48from . import reflection 

49from .base import Connection 

50from .interfaces import CacheStats 

51from .interfaces import DBAPICursor 

52from .interfaces import Dialect 

53from .interfaces import ExecuteStyle 

54from .interfaces import ExecutionContext 

55from .reflection import ObjectKind 

56from .reflection import ObjectScope 

57from .. import event 

58from .. import exc 

59from .. import pool 

60from .. import util 

61from ..sql import compiler 

62from ..sql import dml 

63from ..sql import expression 

64from ..sql import type_api 

65from ..sql import util as sql_util 

66from ..sql._typing import is_tuple_type 

67from ..sql.base import _NoArg 

68from ..sql.compiler import AggregateOrderByStyle 

69from ..sql.compiler import DDLCompiler 

70from ..sql.compiler import InsertmanyvaluesSentinelOpts 

71from ..sql.compiler import SQLCompiler 

72from ..sql.elements import quoted_name 

73from ..util.typing import TupleAny 

74from ..util.typing import Unpack 

75 

76if typing.TYPE_CHECKING: 

77 from .base import Engine 

78 from .cursor import ResultFetchStrategy 

79 from .interfaces import _CoreMultiExecuteParams 

80 from .interfaces import _CoreSingleExecuteParams 

81 from .interfaces import _DBAPICursorDescription 

82 from .interfaces import _DBAPIMultiExecuteParams 

83 from .interfaces import _DBAPISingleExecuteParams 

84 from .interfaces import _ExecuteOptions 

85 from .interfaces import _MutableCoreSingleExecuteParams 

86 from .interfaces import _ParamStyle 

87 from .interfaces import ConnectArgsType 

88 from .interfaces import DBAPIConnection 

89 from .interfaces import DBAPIModule 

90 from .interfaces import DBAPIType 

91 from .interfaces import IsolationLevel 

92 from .interfaces import TableKey 

93 from .row import Row 

94 from .url import URL 

95 from ..event import _ListenerFnType 

96 from ..pool import Pool 

97 from ..pool import PoolProxiedConnection 

98 from ..sql import Executable 

99 from ..sql.compiler import Compiled 

100 from ..sql.compiler import Linting 

101 from ..sql.compiler import ResultColumnsEntry 

102 from ..sql.dml import DMLState 

103 from ..sql.dml import UpdateBase 

104 from ..sql.elements import BindParameter 

105 from ..sql.schema import Column 

106 from ..sql.sqltypes import _JSON_VALUE 

107 from ..sql.type_api import _BindProcessorType 

108 from ..sql.type_api import _ResultProcessorType 

109 from ..sql.type_api import TypeEngine 

110 

111 

112# When we're handed literal SQL, ensure it's a SELECT query 

113SERVER_SIDE_CURSOR_RE = re.compile(r"\s*SELECT", re.I | re.UNICODE) 

114 

115 

116( 

117 CACHE_HIT, 

118 CACHE_MISS, 

119 CACHING_DISABLED, 

120 NO_CACHE_KEY, 

121 NO_DIALECT_SUPPORT, 

122) = list(CacheStats) 

123 

124 

125class _BackendsMultiReflection(Dialect): 

126 """Mixin providing single-table reflection wrappers that delegate to 

127 the corresponding ``get_multi_*`` methods. 

128 

129 Used by dialects that implement native multi-table reflection 

130 (PostgreSQL, Oracle, MSSQL). 

131 """ 

132 

133 @reflection.cache 

134 def has_table( 

135 self, 

136 connection: Connection, 

137 table_name: str, 

138 schema: Optional[str] = None, 

139 **kw: Any, 

140 ) -> bool: 

141 # NOTE: assume it's a subclass of DefaultDialect 

142 self._ensure_has_table_connection(connection) # type: ignore[attr-defined] # noqa: E501 

143 multi_res = self.has_multi_table( 

144 connection, 

145 table_names=[table_name], 

146 schema=schema, 

147 **kw, 

148 ) 

149 # has_multi_table returns all the input table names so it's not 

150 # possible for the key to be missing 

151 return dict(multi_res)[(schema, table_name)] 

152 

153 def _value_or_raise(self, data, table, schema): 

154 try: 

155 return dict(data)[(schema, table)] 

156 except KeyError: 

157 raise exc.NoSuchTableError( 

158 f"{schema}.{table}" if schema else table 

159 ) from None 

160 

161 @reflection.cache 

162 def get_columns(self, connection, table_name, schema=None, **kw): 

163 data = self.get_multi_columns( 

164 connection, 

165 schema=schema, 

166 filter_names=[table_name], 

167 scope=ObjectScope.ANY, 

168 kind=ObjectKind.ANY, 

169 **kw, 

170 ) 

171 return self._value_or_raise(data, table_name, schema) 

172 

173 @reflection.cache 

174 def get_table_options(self, connection, table_name, schema=None, **kw): 

175 data = self.get_multi_table_options( 

176 connection, 

177 schema=schema, 

178 filter_names=[table_name], 

179 scope=ObjectScope.ANY, 

180 kind=ObjectKind.ANY, 

181 **kw, 

182 ) 

183 return self._value_or_raise(data, table_name, schema) 

184 

185 @reflection.cache 

186 def get_pk_constraint(self, connection, table_name, schema=None, **kw): 

187 data = self.get_multi_pk_constraint( 

188 connection, 

189 schema=schema, 

190 filter_names=[table_name], 

191 scope=ObjectScope.ANY, 

192 kind=ObjectKind.ANY, 

193 **kw, 

194 ) 

195 return self._value_or_raise(data, table_name, schema) 

196 

197 @reflection.cache 

198 def get_foreign_keys(self, connection, table_name, schema=None, **kw): 

199 data = self.get_multi_foreign_keys( 

200 connection, 

201 schema=schema, 

202 filter_names=[table_name], 

203 scope=ObjectScope.ANY, 

204 kind=ObjectKind.ANY, 

205 **kw, 

206 ) 

207 return self._value_or_raise(data, table_name, schema) 

208 

209 @reflection.cache 

210 def get_indexes(self, connection, table_name, schema=None, **kw): 

211 data = self.get_multi_indexes( 

212 connection, 

213 schema=schema, 

214 filter_names=[table_name], 

215 scope=ObjectScope.ANY, 

216 kind=ObjectKind.ANY, 

217 **kw, 

218 ) 

219 return self._value_or_raise(data, table_name, schema) 

220 

221 @reflection.cache 

222 def get_unique_constraints( 

223 self, connection, table_name, schema=None, **kw 

224 ): 

225 data = self.get_multi_unique_constraints( 

226 connection, 

227 schema=schema, 

228 filter_names=[table_name], 

229 scope=ObjectScope.ANY, 

230 kind=ObjectKind.ANY, 

231 **kw, 

232 ) 

233 return self._value_or_raise(data, table_name, schema) 

234 

235 @reflection.cache 

236 def get_check_constraints(self, connection, table_name, schema=None, **kw): 

237 data = self.get_multi_check_constraints( 

238 connection, 

239 schema=schema, 

240 filter_names=[table_name], 

241 scope=ObjectScope.ANY, 

242 kind=ObjectKind.ANY, 

243 **kw, 

244 ) 

245 return self._value_or_raise(data, table_name, schema) 

246 

247 @reflection.cache 

248 def get_table_comment(self, connection, table_name, schema=None, **kw): 

249 data = self.get_multi_table_comment( 

250 connection, 

251 schema=schema, 

252 filter_names=[table_name], 

253 scope=ObjectScope.ANY, 

254 kind=ObjectKind.ANY, 

255 **kw, 

256 ) 

257 return self._value_or_raise(data, table_name, schema) 

258 

259 

260class DefaultDialect(Dialect): 

261 """Default implementation of Dialect""" 

262 

263 statement_compiler = compiler.SQLCompiler 

264 ddl_compiler = compiler.DDLCompiler 

265 type_compiler_cls = compiler.GenericTypeCompiler 

266 

267 preparer = compiler.IdentifierPreparer 

268 supports_alter = True 

269 supports_comments = False 

270 supports_constraint_comments = False 

271 inline_comments = False 

272 supports_statement_cache = True 

273 

274 div_is_floordiv = True 

275 

276 bind_typing = interfaces.BindTyping.NONE 

277 

278 include_set_input_sizes: Optional[Set[Any]] = None 

279 exclude_set_input_sizes: Optional[Set[Any]] = None 

280 

281 # the first value we'd get for an autoincrement column. 

282 default_sequence_base = 1 

283 

284 # most DBAPIs happy with this for execute(). 

285 # not cx_oracle. 

286 execute_sequence_format = tuple 

287 

288 supports_schemas = True 

289 supports_views = True 

290 supports_sequences = False 

291 sequences_optional = False 

292 preexecute_autoincrement_sequences = False 

293 supports_identity_columns = False 

294 postfetch_lastrowid = True 

295 favor_returning_over_lastrowid = False 

296 insert_null_pk_still_autoincrements = False 

297 update_returning = False 

298 delete_returning = False 

299 update_returning_multifrom = False 

300 delete_returning_multifrom = False 

301 insert_returning = False 

302 

303 aggregate_order_by_style = AggregateOrderByStyle.INLINE 

304 

305 cte_follows_insert = False 

306 

307 supports_native_enum = False 

308 supports_native_boolean = False 

309 supports_native_uuid = False 

310 returns_native_bytes = False 

311 

312 supports_native_json_serialization = False 

313 supports_native_json_deserialization = False 

314 dialect_injects_custom_json_deserializer = False 

315 _json_serializer: Callable[[_JSON_VALUE], str] | None = None 

316 

317 _json_deserializer: Callable[[str], _JSON_VALUE] | None = None 

318 

319 non_native_boolean_check_constraint = True 

320 

321 supports_simple_order_by_label = True 

322 

323 tuple_in_values = False 

324 

325 connection_characteristics = util.immutabledict( 

326 { 

327 "isolation_level": characteristics.IsolationLevelCharacteristic(), 

328 "logging_token": characteristics.LoggingTokenCharacteristic(), 

329 } 

330 ) 

331 

332 engine_config_types: Mapping[str, Any] = util.immutabledict( 

333 { 

334 "pool_timeout": util.asint, 

335 "echo": util.bool_or_str("debug"), 

336 "echo_pool": util.bool_or_str("debug"), 

337 "pool_recycle": util.asint, 

338 "pool_size": util.asint, 

339 "max_overflow": util.asint, 

340 "future": util.asbool, 

341 } 

342 ) 

343 

344 # if the NUMERIC type 

345 # returns decimal.Decimal. 

346 # *not* the FLOAT type however. 

347 supports_native_decimal = False 

348 

349 name = "default" 

350 

351 # length at which to truncate 

352 # any identifier. 

353 max_identifier_length = 9999 

354 _user_defined_max_identifier_length: Optional[int] = None 

355 

356 isolation_level: Optional[str] = None 

357 

358 # sub-categories of max_identifier_length. 

359 # currently these accommodate for MySQL which allows alias names 

360 # of 255 but DDL names only of 64. 

361 max_index_name_length: Optional[int] = None 

362 max_constraint_name_length: Optional[int] = None 

363 

364 supports_sane_rowcount = True 

365 supports_sane_multi_rowcount = True 

366 colspecs: MutableMapping[Type[TypeEngine[Any]], Type[TypeEngine[Any]]] = {} 

367 default_paramstyle = "named" 

368 

369 supports_default_values = False 

370 """dialect supports INSERT... DEFAULT VALUES syntax""" 

371 

372 supports_default_metavalue = False 

373 """dialect supports INSERT... VALUES (DEFAULT) syntax""" 

374 

375 default_metavalue_token = "DEFAULT" 

376 """for INSERT... VALUES (DEFAULT) syntax, the token to put in the 

377 parenthesis.""" 

378 

379 # not sure if this is a real thing but the compiler will deliver it 

380 # if this is the only flag enabled. 

381 supports_empty_insert = True 

382 """dialect supports INSERT () VALUES ()""" 

383 

384 supports_multivalues_insert = False 

385 

386 use_insertmanyvalues: bool = False 

387 

388 use_insertmanyvalues_wo_returning: bool = False 

389 

390 insertmanyvalues_implicit_sentinel: InsertmanyvaluesSentinelOpts = ( 

391 InsertmanyvaluesSentinelOpts.NOT_SUPPORTED 

392 ) 

393 

394 insertmanyvalues_page_size: int = 1000 

395 insertmanyvalues_max_parameters = 32700 

396 

397 supports_is_distinct_from = True 

398 

399 supports_server_side_cursors = False 

400 

401 server_side_cursors = False 

402 

403 # extra record-level locking features (#4860) 

404 supports_for_update_of = False 

405 

406 server_version_info = None 

407 

408 default_schema_name: Optional[str] = None 

409 

410 # indicates symbol names are 

411 # UPPERCASED if they are case insensitive 

412 # within the database. 

413 # if this is True, the methods normalize_name() 

414 # and denormalize_name() must be provided. 

415 requires_name_normalize = False 

416 

417 is_async = False 

418 

419 has_terminate = False 

420 

421 # TODO: this is not to be part of 2.0. implement rudimentary binary 

422 # literals for SQLite, PostgreSQL, MySQL only within 

423 # _Binary.literal_processor 

424 _legacy_binary_type_literal_encoding = "utf-8" 

425 

426 @util.deprecated_params( 

427 empty_in_strategy=( 

428 "1.4", 

429 "The :paramref:`_sa.create_engine.empty_in_strategy` keyword is " 

430 "deprecated, and no longer has any effect. All IN expressions " 

431 "are now rendered using " 

432 'the "expanding parameter" strategy which renders a set of bound' 

433 'expressions, or an "empty set" SELECT, at statement execution' 

434 "time.", 

435 ), 

436 server_side_cursors=( 

437 "1.4", 

438 "The :paramref:`_sa.create_engine.server_side_cursors` parameter " 

439 "is deprecated and will be removed in a future release. Please " 

440 "use the " 

441 ":paramref:`_engine.Connection.execution_options.stream_results` " 

442 "parameter.", 

443 ), 

444 ) 

445 def __init__( 

446 self, 

447 paramstyle: Optional[_ParamStyle] = None, 

448 isolation_level: Optional[IsolationLevel] = None, 

449 dbapi: Optional[DBAPIModule] = None, 

450 implicit_returning: Literal[True] = True, 

451 supports_native_boolean: Optional[bool] = None, 

452 max_identifier_length: Optional[int] = None, 

453 label_length: Optional[int] = None, 

454 insertmanyvalues_page_size: Union[_NoArg, int] = _NoArg.NO_ARG, 

455 use_insertmanyvalues: Optional[bool] = None, 

456 # util.deprecated_params decorator cannot render the 

457 # Linting.NO_LINTING constant 

458 compiler_linting: Linting = int(compiler.NO_LINTING), # type: ignore[assignment] # noqa: E501 

459 server_side_cursors: bool = False, 

460 skip_autocommit_rollback: bool = False, 

461 **kwargs: Any, 

462 ): 

463 if server_side_cursors: 

464 if not self.supports_server_side_cursors: 

465 raise exc.ArgumentError( 

466 "Dialect %s does not support server side cursors" % self 

467 ) 

468 else: 

469 self.server_side_cursors = True 

470 

471 if getattr(self, "use_setinputsizes", False): 

472 util.warn_deprecated( 

473 "The dialect-level use_setinputsizes attribute is " 

474 "deprecated. Please use " 

475 "bind_typing = BindTyping.SETINPUTSIZES", 

476 "2.0", 

477 ) 

478 self.bind_typing = interfaces.BindTyping.SETINPUTSIZES 

479 

480 self.positional = False 

481 self._ischema = None 

482 

483 self.dbapi = dbapi 

484 

485 self.skip_autocommit_rollback = skip_autocommit_rollback 

486 

487 if paramstyle is not None: 

488 self.paramstyle = paramstyle 

489 elif self.dbapi is not None: 

490 self.paramstyle = self.dbapi.paramstyle 

491 else: 

492 self.paramstyle = self.default_paramstyle 

493 self.positional = self.paramstyle in ( 

494 "qmark", 

495 "format", 

496 "numeric", 

497 "numeric_dollar", 

498 ) 

499 self.identifier_preparer = self.preparer(self) 

500 self._on_connect_isolation_level = isolation_level 

501 

502 legacy_tt_callable = getattr(self, "type_compiler", None) 

503 if legacy_tt_callable is not None: 

504 tt_callable = cast( 

505 Type[compiler.GenericTypeCompiler], 

506 self.type_compiler, 

507 ) 

508 else: 

509 tt_callable = self.type_compiler_cls 

510 

511 self.type_compiler_instance = self.type_compiler = tt_callable(self) 

512 

513 if supports_native_boolean is not None: 

514 self.supports_native_boolean = supports_native_boolean 

515 

516 self._user_defined_max_identifier_length = max_identifier_length 

517 if self._user_defined_max_identifier_length: 

518 self.max_identifier_length = ( 

519 self._user_defined_max_identifier_length 

520 ) 

521 self.label_length = label_length 

522 self.compiler_linting = compiler_linting 

523 

524 if use_insertmanyvalues is not None: 

525 self.use_insertmanyvalues = use_insertmanyvalues 

526 

527 if insertmanyvalues_page_size is not _NoArg.NO_ARG: 

528 self.insertmanyvalues_page_size = insertmanyvalues_page_size 

529 

530 self._check_minimum_dbapi_version() 

531 

532 @property 

533 @util.deprecated( 

534 "2.0", 

535 "full_returning is deprecated, please use insert_returning, " 

536 "update_returning, delete_returning", 

537 ) 

538 def full_returning(self): 

539 return ( 

540 self.insert_returning 

541 and self.update_returning 

542 and self.delete_returning 

543 ) 

544 

545 @util.memoized_property 

546 def insert_executemany_returning(self): 

547 """Default implementation for insert_executemany_returning, if not 

548 otherwise overridden by the specific dialect. 

549 

550 The default dialect determines "insert_executemany_returning" is 

551 available if the dialect in use has opted into using the 

552 "use_insertmanyvalues" feature. If they haven't opted into that, then 

553 this attribute is False, unless the dialect in question overrides this 

554 and provides some other implementation (such as the Oracle Database 

555 dialects). 

556 

557 """ 

558 return self.insert_returning and self.use_insertmanyvalues 

559 

560 @util.memoized_property 

561 def insert_executemany_returning_sort_by_parameter_order(self): 

562 """Default implementation for 

563 insert_executemany_returning_deterministic_order, if not otherwise 

564 overridden by the specific dialect. 

565 

566 The default dialect determines "insert_executemany_returning" can have 

567 deterministic order only if the dialect in use has opted into using the 

568 "use_insertmanyvalues" feature, which implements deterministic ordering 

569 using client side sentinel columns only by default. The 

570 "insertmanyvalues" feature also features alternate forms that can 

571 use server-generated PK values as "sentinels", but those are only 

572 used if the :attr:`.Dialect.insertmanyvalues_implicit_sentinel` 

573 bitflag enables those alternate SQL forms, which are disabled 

574 by default. 

575 

576 If the dialect in use hasn't opted into that, then this attribute is 

577 False, unless the dialect in question overrides this and provides some 

578 other implementation (such as the Oracle Database dialects). 

579 

580 """ 

581 return self.insert_returning and self.use_insertmanyvalues 

582 

583 update_executemany_returning = False 

584 delete_executemany_returning = False 

585 

586 @util.memoized_property 

587 def loaded_dbapi(self) -> DBAPIModule: 

588 if self.dbapi is None: 

589 raise exc.NoDBAPILoaded( 

590 f"Dialect {self} does not have a Python DBAPI established " 

591 "and cannot be used for actual database interaction" 

592 ) 

593 return self.dbapi 

594 

595 @util.memoized_property 

596 def dbapi_version(self) -> util.VersionInfo: 

597 # memoization applies to a successfully determined version only; 

598 # memoized_property does not cache when the function raises, so a 

599 # DBAPI which is established after this dialect was constructed is 

600 # still picked up 

601 if self.dbapi is None: 

602 raise exc.NoDBAPILoaded( 

603 f"Dialect {self.name}+{self.driver} has no DBAPI module " 

604 "loaded; no DBAPI version is available" 

605 ) 

606 

607 try: 

608 version = self.retrieve_dbapi_version(self.dbapi) 

609 except NotImplementedError as ne: 

610 raise NotImplementedError( 

611 f"Dialect {self.name}+{self.driver} does not implement " 

612 "retrieve_dbapi_version(); no DBAPI version is available" 

613 ) from ne 

614 

615 if not version: 

616 # the DBAPI is loaded but publishes no version of its own; as 

617 # with a DBAPI that isn't loaded, this is not an error on the 

618 # part of the dialect 

619 # asyncio dialects have a wrapper object here rather than a 

620 # module, which has no __name__ 

621 dbapi_name = getattr(self.dbapi, "__name__", self.driver) 

622 raise exc.NoDBAPILoaded( 

623 f"Dialect {self.name}+{self.driver} could not determine a " 

624 f"version for its DBAPI module {dbapi_name!r}" 

625 ) 

626 

627 return version 

628 

629 @property 

630 def _dbapi_version_or_none(self) -> Optional[util.VersionInfo]: 

631 """:attr:`.Dialect.dbapi_version`, or None if it can't be 

632 determined. 

633 

634 For use by dialect startup checks, which run before a DBAPI is 

635 necessarily present and which must not fail when no version is 

636 available. Only :class:`.exc.NoDBAPILoaded` is accommodated; 

637 ``NotImplementedError``, indicating a dialect which does not 

638 implement :meth:`.Dialect.retrieve_dbapi_version` at all, is a bug 

639 in that dialect and is allowed to propagate. Deliberately not 

640 memoized itself; the underlying :attr:`.Dialect.dbapi_version` 

641 memoizes the success case. 

642 

643 """ 

644 try: 

645 return self.dbapi_version 

646 except exc.NoDBAPILoaded: 

647 return None 

648 

649 def _check_minimum_dbapi_version(self) -> None: 

650 """Enforce :attr:`.Dialect.minimum_dbapi_version`, if present. 

651 

652 Takes place as the dialect is constructed. No check occurs when 

653 the version of the DBAPI is not available at all. 

654 

655 """ 

656 minimum = self.minimum_dbapi_version 

657 if minimum is None: 

658 return 

659 

660 version = self._dbapi_version_or_none 

661 if version is not None and version < minimum: 

662 dbapi_name = getattr(self.dbapi, "__name__", self.driver) 

663 raise exc.InvalidRequestError( 

664 f"Dialect {self.name}+{self.driver} requires version " 

665 f"{minimum} or greater of the {dbapi_name} DBAPI; " 

666 f"version {version} is installed" 

667 ) 

668 

669 @util.memoized_property 

670 def _bind_typing_render_casts(self): 

671 return self.bind_typing is interfaces.BindTyping.RENDER_CASTS 

672 

673 def _ensure_has_table_connection(self, arg: Connection) -> None: 

674 if not isinstance(arg, Connection): 

675 raise exc.ArgumentError( 

676 "The argument passed to Dialect.has_table() should be a " 

677 "%s, got %s. " 

678 "Additionally, the Dialect.has_table() method is for " 

679 "internal dialect " 

680 "use only; please use " 

681 "``inspect(some_engine).has_table(<tablename>>)`` " 

682 "for public API use." % (Connection, type(arg)) 

683 ) 

684 

685 @util.memoized_property 

686 def _supports_statement_cache(self): 

687 ssc = self.__class__.__dict__.get("supports_statement_cache", None) 

688 if ssc is None: 

689 util.warn( 

690 "Dialect %s:%s will not make use of SQL compilation caching " 

691 "as it does not set the 'supports_statement_cache' attribute " 

692 "to ``True``. This can have " 

693 "significant performance implications including some " 

694 "performance degradations in comparison to prior SQLAlchemy " 

695 "versions. Dialect maintainers should seek to set this " 

696 "attribute to True after appropriate development and testing " 

697 "for SQLAlchemy 1.4 caching support. Alternatively, this " 

698 "attribute may be set to False which will disable this " 

699 "warning." % (self.name, self.driver), 

700 code="cprf", 

701 ) 

702 

703 return bool(ssc) 

704 

705 @util.memoized_property 

706 def _type_memos(self): 

707 return weakref.WeakKeyDictionary() 

708 

709 @property 

710 def dialect_description(self): # type: ignore[override] 

711 return self.name + "+" + self.driver 

712 

713 @property 

714 def supports_sane_rowcount_returning(self): 

715 """True if this dialect supports sane rowcount even if RETURNING is 

716 in use. 

717 

718 For dialects that don't support RETURNING, this is synonymous with 

719 ``supports_sane_rowcount``. 

720 

721 """ 

722 return self.supports_sane_rowcount 

723 

724 @classmethod 

725 def get_pool_class(cls, url: URL) -> Type[Pool]: 

726 default: Type[pool.Pool] 

727 if cls.is_async: 

728 default = pool.AsyncAdaptedQueuePool 

729 else: 

730 default = pool.QueuePool 

731 

732 return getattr(cls, "poolclass", default) 

733 

734 def get_dialect_pool_class(self, url: URL) -> Type[Pool]: 

735 return self.get_pool_class(url) 

736 

737 @classmethod 

738 def load_provisioning(cls): 

739 package = ".".join(cls.__module__.split(".")[0:-1]) 

740 try: 

741 __import__(package + ".provision") 

742 except ImportError: 

743 pass 

744 

745 def _builtin_onconnect(self) -> Optional[_ListenerFnType]: 

746 if self._on_connect_isolation_level is not None: 

747 

748 def builtin_connect(dbapi_conn, conn_rec): 

749 self._assert_and_set_isolation_level( 

750 dbapi_conn, self._on_connect_isolation_level 

751 ) 

752 

753 return builtin_connect 

754 else: 

755 return None 

756 

757 def initialize(self, connection: Connection) -> None: 

758 try: 

759 self.server_version_info = self._get_server_version_info( 

760 connection 

761 ) 

762 except NotImplementedError: 

763 self.server_version_info = None 

764 try: 

765 self.default_schema_name = self._get_default_schema_name( 

766 connection 

767 ) 

768 except NotImplementedError: 

769 self.default_schema_name = None 

770 

771 try: 

772 self.default_isolation_level = self.get_default_isolation_level( 

773 connection.connection.dbapi_connection 

774 ) 

775 except NotImplementedError: 

776 self.default_isolation_level = None 

777 

778 if not self._user_defined_max_identifier_length: 

779 max_ident_length = self._check_max_identifier_length(connection) 

780 if max_ident_length: 

781 self.max_identifier_length = max_ident_length 

782 

783 if ( 

784 self.label_length 

785 and self.label_length > self.max_identifier_length 

786 ): 

787 raise exc.ArgumentError( 

788 "Label length of %d is greater than this dialect's" 

789 " maximum identifier length of %d" 

790 % (self.label_length, self.max_identifier_length) 

791 ) 

792 

793 def on_connect(self) -> Optional[Callable[[Any], None]]: 

794 # inherits the docstring from interfaces.Dialect.on_connect 

795 return None 

796 

797 def _check_max_identifier_length(self, connection): 

798 """Perform a connection / server version specific check to determine 

799 the max_identifier_length. 

800 

801 If the dialect's class level max_identifier_length should be used, 

802 can return None. 

803 

804 """ 

805 return None 

806 

807 def get_default_isolation_level(self, dbapi_conn): 

808 """Given a DBAPI connection, return its isolation level, or 

809 a default isolation level if one cannot be retrieved. 

810 

811 May be overridden by subclasses in order to provide a 

812 "fallback" isolation level for databases that cannot reliably 

813 retrieve the actual isolation level. 

814 

815 By default, calls the :meth:`_engine.Interfaces.get_isolation_level` 

816 method, propagating any exceptions raised. 

817 

818 """ 

819 return self.get_isolation_level(dbapi_conn) 

820 

821 def type_descriptor(self, typeobj): 

822 """Provide a database-specific :class:`.TypeEngine` object, given 

823 the generic object which comes from the types module. 

824 

825 This method looks for a dictionary called 

826 ``colspecs`` as a class or instance-level variable, 

827 and passes on to :func:`_types.adapt_type`. 

828 

829 """ 

830 return type_api.adapt_type(typeobj, self.colspecs) 

831 

832 def has_index(self, connection, table_name, index_name, schema=None, **kw): 

833 if not self.has_table(connection, table_name, schema=schema, **kw): 

834 return False 

835 for idx in self.get_indexes( 

836 connection, table_name, schema=schema, **kw 

837 ): 

838 if idx["name"] == index_name: 

839 return True 

840 else: 

841 return False 

842 

843 def has_schema( 

844 self, connection: Connection, schema_name: str, **kw: Any 

845 ) -> bool: 

846 return schema_name in self.get_schema_names(connection, **kw) 

847 

848 def validate_identifier(self, ident: str) -> None: 

849 if len(ident) > self.max_identifier_length: 

850 raise exc.IdentifierError( 

851 "Identifier '%s' exceeds maximum length of %d characters" 

852 % (ident, self.max_identifier_length) 

853 ) 

854 

855 def connect(self, *cargs: Any, **cparams: Any) -> DBAPIConnection: 

856 # inherits the docstring from interfaces.Dialect.connect 

857 return self.loaded_dbapi.connect(*cargs, **cparams) # type: ignore[no-any-return] # NOQA: E501 

858 

859 def create_connect_args(self, url: URL) -> ConnectArgsType: 

860 # inherits the docstring from interfaces.Dialect.create_connect_args 

861 opts = url.translate_connect_args() 

862 opts.update(url.query) 

863 return ([], opts) 

864 

865 def set_engine_execution_options( 

866 self, engine: Engine, opts: Mapping[str, Any] 

867 ) -> None: 

868 supported_names = set(self.connection_characteristics).intersection( 

869 opts 

870 ) 

871 if supported_names: 

872 characteristics: Mapping[str, Any] = util.immutabledict( 

873 (name, opts[name]) for name in supported_names 

874 ) 

875 

876 @event.listens_for(engine, "engine_connect") 

877 def set_connection_characteristics(connection): 

878 self._set_connection_characteristics( 

879 connection, characteristics 

880 ) 

881 

882 def set_connection_execution_options( 

883 self, connection: Connection, opts: Mapping[str, Any] 

884 ) -> None: 

885 supported_names = set(self.connection_characteristics).intersection( 

886 opts 

887 ) 

888 if supported_names: 

889 characteristics: Mapping[str, Any] = util.immutabledict( 

890 (name, opts[name]) for name in supported_names 

891 ) 

892 self._set_connection_characteristics(connection, characteristics) 

893 

894 def _set_connection_characteristics(self, connection, characteristics): 

895 characteristic_values = [ 

896 (name, self.connection_characteristics[name], value) 

897 for name, value in characteristics.items() 

898 ] 

899 

900 if connection.in_transaction(): 

901 trans_objs = [ 

902 (name, obj) 

903 for name, obj, _ in characteristic_values 

904 if obj.transactional 

905 ] 

906 if trans_objs: 

907 raise exc.InvalidRequestError( 

908 "This connection has already initialized a SQLAlchemy " 

909 "Transaction() object via begin() or autobegin; " 

910 "%s may not be altered unless rollback() or commit() " 

911 "is called first." 

912 % (", ".join(name for name, obj in trans_objs)) 

913 ) 

914 

915 dbapi_connection = connection.connection.dbapi_connection 

916 for _, characteristic, value in characteristic_values: 

917 characteristic.set_connection_characteristic( 

918 self, connection, dbapi_connection, value 

919 ) 

920 connection.connection._connection_record.finalize_callback.append( 

921 functools.partial(self._reset_characteristics, characteristics) 

922 ) 

923 

924 def _reset_characteristics(self, characteristics, dbapi_connection): 

925 for characteristic_name in characteristics: 

926 characteristic = self.connection_characteristics[ 

927 characteristic_name 

928 ] 

929 characteristic.reset_characteristic(self, dbapi_connection) 

930 

931 def do_begin(self, dbapi_connection): 

932 pass 

933 

934 def do_rollback(self, dbapi_connection): 

935 if self.skip_autocommit_rollback and self.detect_autocommit_setting( 

936 dbapi_connection 

937 ): 

938 return 

939 dbapi_connection.rollback() 

940 

941 def do_commit(self, dbapi_connection): 

942 dbapi_connection.commit() 

943 

944 def do_terminate(self, dbapi_connection): 

945 self.do_close(dbapi_connection) 

946 

947 def do_close(self, dbapi_connection): 

948 dbapi_connection.close() 

949 

950 @util.memoized_property 

951 def _dialect_specific_select_one(self): 

952 return str(expression.select(1).compile(dialect=self)) 

953 

954 def _do_ping_w_event(self, dbapi_connection: DBAPIConnection) -> bool: 

955 try: 

956 return self.do_ping(dbapi_connection) 

957 except self.loaded_dbapi.Error as err: 

958 is_disconnect = self.is_disconnect(err, dbapi_connection, None) 

959 

960 if self._has_events: 

961 try: 

962 Connection._handle_dbapi_exception_noconnection( 

963 err, 

964 self, 

965 is_disconnect=is_disconnect, 

966 invalidate_pool_on_disconnect=False, 

967 is_pre_ping=True, 

968 ) 

969 except exc.StatementError as new_err: 

970 is_disconnect = new_err.connection_invalidated 

971 

972 if is_disconnect: 

973 return False 

974 else: 

975 raise 

976 

977 def do_ping(self, dbapi_connection: DBAPIConnection) -> bool: 

978 cursor = dbapi_connection.cursor() 

979 try: 

980 cursor.execute(self._dialect_specific_select_one) 

981 finally: 

982 cursor.close() 

983 return True 

984 

985 def create_xid(self): 

986 """Create a random two-phase transaction ID. 

987 

988 This id will be passed to do_begin_twophase(), do_rollback_twophase(), 

989 do_commit_twophase(). Its format is unspecified. 

990 """ 

991 

992 return "_sa_%032x" % random.randint(0, 2**128) 

993 

994 def do_savepoint(self, connection, name): 

995 connection.execute(expression.SavepointClause(name)) 

996 

997 def do_rollback_to_savepoint(self, connection, name): 

998 connection.execute(expression.RollbackToSavepointClause(name)) 

999 

1000 def do_release_savepoint(self, connection, name): 

1001 connection.execute(expression.ReleaseSavepointClause(name)) 

1002 

1003 def _deliver_insertmanyvalues_batches( 

1004 self, 

1005 connection, 

1006 cursor, 

1007 statement, 

1008 parameters, 

1009 generic_setinputsizes, 

1010 context, 

1011 ): 

1012 context = cast(DefaultExecutionContext, context) 

1013 compiled = cast(SQLCompiler, context.compiled) 

1014 

1015 _composite_sentinel_proc: Sequence[ 

1016 Optional[_ResultProcessorType[Any]] 

1017 ] = () 

1018 _scalar_sentinel_proc: Optional[_ResultProcessorType[Any]] = None 

1019 _sentinel_proc_initialized: bool = False 

1020 

1021 compiled_parameters = context.compiled_parameters 

1022 

1023 imv = compiled._insertmanyvalues 

1024 assert imv is not None 

1025 

1026 is_returning: Final[bool] = bool(compiled.effective_returning) 

1027 batch_size = context.execution_options.get( 

1028 "insertmanyvalues_page_size", self.insertmanyvalues_page_size 

1029 ) 

1030 

1031 if compiled.schema_translate_map: 

1032 schema_translate_map = context.execution_options.get( 

1033 "schema_translate_map", {} 

1034 ) 

1035 else: 

1036 schema_translate_map = None 

1037 

1038 if is_returning: 

1039 result: Optional[List[Any]] = [] 

1040 context._insertmanyvalues_rows = result 

1041 

1042 sort_by_parameter_order = imv.sort_by_parameter_order 

1043 

1044 else: 

1045 sort_by_parameter_order = False 

1046 result = None 

1047 

1048 for imv_batch in compiled._deliver_insertmanyvalues_batches( 

1049 statement, 

1050 parameters, 

1051 compiled_parameters, 

1052 generic_setinputsizes, 

1053 batch_size, 

1054 sort_by_parameter_order, 

1055 schema_translate_map, 

1056 ): 

1057 yield imv_batch 

1058 

1059 if is_returning: 

1060 

1061 try: 

1062 rows = context.fetchall_for_returning(cursor) 

1063 except BaseException as be: 

1064 connection._handle_dbapi_exception( 

1065 be, 

1066 sql_util._long_statement(imv_batch.replaced_statement), 

1067 imv_batch.replaced_parameters, 

1068 None, 

1069 context, 

1070 is_sub_exec=True, 

1071 ) 

1072 

1073 # I would have thought "is_returning: Final[bool]" 

1074 # would have assured this but pylance thinks not 

1075 assert result is not None 

1076 

1077 if imv.num_sentinel_columns and not imv_batch.is_downgraded: 

1078 composite_sentinel = imv.num_sentinel_columns > 1 

1079 if imv.implicit_sentinel: 

1080 # for implicit sentinel, which is currently single-col 

1081 # integer autoincrement, do a simple sort. 

1082 assert not composite_sentinel 

1083 result.extend( 

1084 sorted(rows, key=operator.itemgetter(-1)) 

1085 ) 

1086 continue 

1087 

1088 # otherwise, create dictionaries to match up batches 

1089 # with parameters 

1090 assert imv.sentinel_param_keys 

1091 assert imv.sentinel_columns 

1092 

1093 _nsc = imv.num_sentinel_columns 

1094 

1095 if not _sentinel_proc_initialized: 

1096 if composite_sentinel: 

1097 _composite_sentinel_proc = [ 

1098 col.type._cached_result_processor( 

1099 self, cursor_desc[1] 

1100 ) 

1101 for col, cursor_desc in zip( 

1102 imv.sentinel_columns, 

1103 cursor.description[-_nsc:], 

1104 ) 

1105 ] 

1106 else: 

1107 _scalar_sentinel_proc = ( 

1108 imv.sentinel_columns[0] 

1109 ).type._cached_result_processor( 

1110 self, cursor.description[-1][1] 

1111 ) 

1112 _sentinel_proc_initialized = True 

1113 

1114 rows_by_sentinel: Union[ 

1115 Dict[Tuple[Any, ...], Any], 

1116 Dict[Any, Any], 

1117 ] 

1118 

1119 if composite_sentinel: 

1120 rows_by_sentinel = { 

1121 tuple( 

1122 (proc(val) if proc else val) 

1123 for val, proc in zip( 

1124 row[-_nsc:], _composite_sentinel_proc 

1125 ) 

1126 ): row 

1127 for row in rows 

1128 } 

1129 elif _scalar_sentinel_proc: 

1130 rows_by_sentinel = { 

1131 _scalar_sentinel_proc(row[-1]): row for row in rows 

1132 } 

1133 else: 

1134 rows_by_sentinel = {row[-1]: row for row in rows} 

1135 

1136 if len(rows_by_sentinel) != len(imv_batch.batch): 

1137 # see test_insert_exec.py:: 

1138 # IMVSentinelTest::test_sentinel_incorrect_rowcount 

1139 # for coverage / demonstration 

1140 raise exc.InvalidRequestError( 

1141 f"Sentinel-keyed result set did not produce " 

1142 f"correct number of rows {len(imv_batch.batch)}; " 

1143 "produced " 

1144 f"{len(rows_by_sentinel)}. Please ensure the " 

1145 "sentinel column is fully unique and populated in " 

1146 "all cases." 

1147 ) 

1148 

1149 try: 

1150 ordered_rows = [ 

1151 rows_by_sentinel[sentinel_keys] 

1152 for sentinel_keys in imv_batch.sentinel_values 

1153 ] 

1154 except KeyError as ke: 

1155 # see test_insert_exec.py:: 

1156 # IMVSentinelTest::test_sentinel_cant_match_keys 

1157 # for coverage / demonstration 

1158 raise exc.InvalidRequestError( 

1159 f"Can't match sentinel values in result set to " 

1160 f"parameter sets; key {ke.args[0]!r} was not " 

1161 "found. " 

1162 "There may be a mismatch between the datatype " 

1163 "passed to the DBAPI driver vs. that which it " 

1164 "returns in a result row. Ensure the given " 

1165 "Python value matches the expected result type " 

1166 "*exactly*, taking care to not rely upon implicit " 

1167 "conversions which may occur such as when using " 

1168 "strings in place of UUID or integer values, etc. " 

1169 ) from ke 

1170 

1171 result.extend(ordered_rows) 

1172 

1173 else: 

1174 result.extend(rows) 

1175 

1176 def do_executemany(self, cursor, statement, parameters, context=None): 

1177 cursor.executemany(statement, parameters) 

1178 

1179 def do_execute(self, cursor, statement, parameters, context=None): 

1180 cursor.execute(statement, parameters) 

1181 

1182 def do_execute_no_params(self, cursor, statement, context=None): 

1183 cursor.execute(statement) 

1184 

1185 def is_disconnect( 

1186 self, 

1187 e: DBAPIModule.Error, 

1188 connection: Union[ 

1189 pool.PoolProxiedConnection, interfaces.DBAPIConnection, None 

1190 ], 

1191 cursor: Optional[interfaces.DBAPICursor], 

1192 ) -> bool: 

1193 return False 

1194 

1195 @util.memoized_instancemethod 

1196 def _gen_allowed_isolation_levels(self, dbapi_conn): 

1197 try: 

1198 raw_levels = list(self.get_isolation_level_values(dbapi_conn)) 

1199 except NotImplementedError: 

1200 return None 

1201 else: 

1202 normalized_levels = [ 

1203 level.replace("_", " ").upper() for level in raw_levels 

1204 ] 

1205 if raw_levels != normalized_levels: 

1206 raise ValueError( 

1207 f"Dialect {self.name!r} get_isolation_level_values() " 

1208 f"method should return names as UPPERCASE using spaces, " 

1209 f"not underscores; got " 

1210 f"{sorted(set(raw_levels).difference(normalized_levels))}" 

1211 ) 

1212 return tuple(normalized_levels) 

1213 

1214 def _assert_and_set_isolation_level(self, dbapi_conn, level): 

1215 level = level.replace("_", " ").upper() 

1216 

1217 _allowed_isolation_levels = self._gen_allowed_isolation_levels( 

1218 dbapi_conn 

1219 ) 

1220 if ( 

1221 _allowed_isolation_levels 

1222 and level not in _allowed_isolation_levels 

1223 ): 

1224 raise exc.ArgumentError( 

1225 f"Invalid value {level!r} for isolation_level. " 

1226 f"Valid isolation levels for {self.name!r} are " 

1227 f"{', '.join(_allowed_isolation_levels)}" 

1228 ) 

1229 

1230 self.set_isolation_level(dbapi_conn, level) 

1231 

1232 def reset_isolation_level(self, dbapi_conn): 

1233 if self._on_connect_isolation_level is not None: 

1234 assert ( 

1235 self._on_connect_isolation_level == "AUTOCOMMIT" 

1236 or self._on_connect_isolation_level 

1237 == self.default_isolation_level 

1238 ) 

1239 self._assert_and_set_isolation_level( 

1240 dbapi_conn, self._on_connect_isolation_level 

1241 ) 

1242 else: 

1243 assert self.default_isolation_level is not None 

1244 self._assert_and_set_isolation_level( 

1245 dbapi_conn, 

1246 self.default_isolation_level, 

1247 ) 

1248 

1249 def normalize_name(self, name): 

1250 if name is None: 

1251 return None 

1252 

1253 name_lower = name.lower() 

1254 name_upper = name.upper() 

1255 

1256 if name_upper == name_lower: 

1257 # name has no upper/lower conversion, e.g. non-european characters. 

1258 # return unchanged 

1259 return name 

1260 elif name_upper == name and not ( 

1261 self.identifier_preparer._requires_quotes 

1262 )(name_lower): 

1263 # name is all uppercase and doesn't require quoting; normalize 

1264 # to all lower case 

1265 return name_lower 

1266 elif name_lower == name: 

1267 # name is all lower case, which if denormalized means we need to 

1268 # force quoting on it 

1269 return quoted_name(name, quote=True) 

1270 else: 

1271 # name is mixed case, means it will be quoted in SQL when used 

1272 # later, no normalizes 

1273 return name 

1274 

1275 def denormalize_name(self, name): 

1276 if name is None: 

1277 return None 

1278 

1279 name_lower = name.lower() 

1280 name_upper = name.upper() 

1281 

1282 if name_upper == name_lower: 

1283 # name has no upper/lower conversion, e.g. non-european characters. 

1284 # return unchanged 

1285 return name 

1286 elif name_lower == name and not ( 

1287 self.identifier_preparer._requires_quotes 

1288 )(name_lower): 

1289 name = name_upper 

1290 return name 

1291 

1292 def get_driver_connection(self, connection: DBAPIConnection) -> Any: 

1293 return connection 

1294 

1295 def _overrides_default(self, method): 

1296 return ( 

1297 getattr(type(self), method).__code__ 

1298 is not getattr(DefaultDialect, method).__code__ 

1299 ) 

1300 

1301 def _default_multi_reflect( 

1302 self, 

1303 single_tbl_method, 

1304 connection, 

1305 kind, 

1306 schema, 

1307 filter_names, 

1308 scope, 

1309 **kw, 

1310 ): 

1311 names_fns = [] 

1312 temp_names_fns = [] 

1313 if ObjectKind.TABLE in kind: 

1314 names_fns.append(self.get_table_names) 

1315 temp_names_fns.append(self.get_temp_table_names) 

1316 if ObjectKind.VIEW in kind: 

1317 names_fns.append(self.get_view_names) 

1318 temp_names_fns.append(self.get_temp_view_names) 

1319 if ObjectKind.MATERIALIZED_VIEW in kind: 

1320 names_fns.append(self.get_materialized_view_names) 

1321 # no temp materialized view at the moment 

1322 # temp_names_fns.append(self.get_temp_materialized_view_names) 

1323 

1324 unreflectable = kw.pop("unreflectable", {}) 

1325 

1326 if ( 

1327 filter_names 

1328 and scope is ObjectScope.ANY 

1329 and kind is ObjectKind.ANY 

1330 ): 

1331 # if names are given and no qualification on type of table 

1332 # (i.e. the Table(..., autoload) case), take the names as given, 

1333 # don't run names queries. If a table does not exit 

1334 # NoSuchTableError is raised and it's skipped 

1335 

1336 # this also suits the case for mssql where we can reflect 

1337 # individual temp tables but there's no temp_names_fn 

1338 names = filter_names 

1339 else: 

1340 names = [] 

1341 name_kw = {"schema": schema, **kw} 

1342 fns = [] 

1343 if ObjectScope.DEFAULT in scope: 

1344 fns.extend(names_fns) 

1345 if ObjectScope.TEMPORARY in scope: 

1346 fns.extend(temp_names_fns) 

1347 

1348 for fn in fns: 

1349 try: 

1350 names.extend(fn(connection, **name_kw)) 

1351 except NotImplementedError: 

1352 pass 

1353 

1354 if filter_names: 

1355 filter_names = set(filter_names) 

1356 

1357 # iterate over all the tables/views and call the single table method 

1358 for table in names: 

1359 if not filter_names or table in filter_names: 

1360 key = (schema, table) 

1361 try: 

1362 yield ( 

1363 key, 

1364 single_tbl_method( 

1365 connection, table, schema=schema, **kw 

1366 ), 

1367 ) 

1368 except exc.UnreflectableTableError as err: 

1369 if key not in unreflectable: 

1370 unreflectable[key] = err 

1371 except exc.NoSuchTableError: 

1372 pass 

1373 

1374 def has_multi_table( 

1375 self, 

1376 connection: Connection, 

1377 table_names: Sequence[str], 

1378 schema: Optional[str] = None, 

1379 **kw: Any, 

1380 ) -> Iterable[tuple[TableKey, bool]]: 

1381 for table_name in table_names: 

1382 exist = self.has_table(connection, table_name, schema=schema, **kw) 

1383 yield (schema, table_name), exist 

1384 

1385 def get_multi_table_options(self, connection, **kw): 

1386 return self._default_multi_reflect( 

1387 self.get_table_options, connection, **kw 

1388 ) 

1389 

1390 def get_multi_columns(self, connection, **kw): 

1391 return self._default_multi_reflect(self.get_columns, connection, **kw) 

1392 

1393 def get_multi_pk_constraint(self, connection, **kw): 

1394 return self._default_multi_reflect( 

1395 self.get_pk_constraint, connection, **kw 

1396 ) 

1397 

1398 def get_multi_foreign_keys(self, connection, **kw): 

1399 return self._default_multi_reflect( 

1400 self.get_foreign_keys, connection, **kw 

1401 ) 

1402 

1403 def get_multi_indexes(self, connection, **kw): 

1404 return self._default_multi_reflect(self.get_indexes, connection, **kw) 

1405 

1406 def get_multi_unique_constraints(self, connection, **kw): 

1407 return self._default_multi_reflect( 

1408 self.get_unique_constraints, connection, **kw 

1409 ) 

1410 

1411 def get_multi_check_constraints(self, connection, **kw): 

1412 return self._default_multi_reflect( 

1413 self.get_check_constraints, connection, **kw 

1414 ) 

1415 

1416 def get_multi_table_comment(self, connection, **kw): 

1417 return self._default_multi_reflect( 

1418 self.get_table_comment, connection, **kw 

1419 ) 

1420 

1421 

1422class StrCompileDialect(DefaultDialect): 

1423 statement_compiler = compiler.StrSQLCompiler 

1424 ddl_compiler = compiler.DDLCompiler 

1425 type_compiler_cls = compiler.StrSQLTypeCompiler 

1426 preparer = compiler.IdentifierPreparer 

1427 

1428 insert_returning = True 

1429 update_returning = True 

1430 delete_returning = True 

1431 

1432 supports_statement_cache = True 

1433 

1434 supports_identity_columns = True 

1435 

1436 supports_sequences = True 

1437 sequences_optional = True 

1438 preexecute_autoincrement_sequences = False 

1439 

1440 supports_native_boolean = True 

1441 

1442 supports_multivalues_insert = True 

1443 supports_simple_order_by_label = True 

1444 

1445 

1446class DefaultExecutionContext(ExecutionContext): 

1447 isinsert = False 

1448 isupdate = False 

1449 isdelete = False 

1450 is_crud = False 

1451 is_text = False 

1452 isddl = False 

1453 

1454 execute_style: ExecuteStyle = ExecuteStyle.EXECUTE 

1455 

1456 compiled: Optional[Compiled] = None 

1457 result_column_struct: Optional[ 

1458 Tuple[List[ResultColumnsEntry], bool, bool, bool, bool] 

1459 ] = None 

1460 returned_default_rows: Optional[Sequence[Row[Unpack[TupleAny]]]] = None 

1461 

1462 execution_options: _ExecuteOptions = util.EMPTY_DICT 

1463 

1464 cursor_fetch_strategy = _cursor._DEFAULT_FETCH 

1465 

1466 invoked_statement: Optional[Executable] = None 

1467 

1468 _is_implicit_returning = False 

1469 _is_explicit_returning = False 

1470 _is_supplemental_returning = False 

1471 _is_server_side = False 

1472 

1473 _soft_closed = False 

1474 

1475 _rowcount: Optional[int] = None 

1476 

1477 # a hook for SQLite's translation of 

1478 # result column names 

1479 # NOTE: pyhive is using this hook, can't remove it :( 

1480 _translate_colname: Optional[ 

1481 Callable[[str], Tuple[str, Optional[str]]] 

1482 ] = None 

1483 

1484 _expanded_parameters: Mapping[str, List[str]] = util.immutabledict() 

1485 """used by set_input_sizes(). 

1486 

1487 This collection comes from ``ExpandedState.parameter_expansion``. 

1488 

1489 """ 

1490 

1491 cache_hit = NO_CACHE_KEY 

1492 

1493 root_connection: Connection 

1494 _dbapi_connection: PoolProxiedConnection 

1495 dialect: Dialect 

1496 unicode_statement: str 

1497 cursor: DBAPICursor 

1498 compiled_parameters: List[_MutableCoreSingleExecuteParams] 

1499 parameters: _DBAPIMultiExecuteParams 

1500 extracted_parameters: Optional[Sequence[BindParameter[Any]]] 

1501 

1502 _empty_dict_params = cast("Mapping[str, Any]", util.EMPTY_DICT) 

1503 

1504 _insertmanyvalues_rows: Optional[List[Tuple[Any, ...]]] = None 

1505 _num_sentinel_cols: int = 0 

1506 

1507 @classmethod 

1508 def _init_ddl( 

1509 cls, 

1510 dialect: Dialect, 

1511 connection: Connection, 

1512 dbapi_connection: PoolProxiedConnection, 

1513 execution_options: _ExecuteOptions, 

1514 compiled_ddl: DDLCompiler, 

1515 ) -> ExecutionContext: 

1516 """Initialize execution context for an ExecutableDDLElement 

1517 construct.""" 

1518 

1519 self = cls.__new__(cls) 

1520 self.root_connection = connection 

1521 self._dbapi_connection = dbapi_connection 

1522 self.dialect = connection.dialect 

1523 

1524 self.compiled = compiled = compiled_ddl 

1525 self.isddl = True 

1526 

1527 self.execution_options = execution_options 

1528 

1529 self.unicode_statement = str(compiled) 

1530 if compiled.schema_translate_map: 

1531 schema_translate_map = self.execution_options.get( 

1532 "schema_translate_map", {} 

1533 ) 

1534 

1535 rst = compiled.preparer._render_schema_translates 

1536 self.unicode_statement = rst( 

1537 self.unicode_statement, schema_translate_map 

1538 ) 

1539 

1540 self.statement = self.unicode_statement 

1541 

1542 self.cursor = self.create_cursor() 

1543 self.compiled_parameters = [] 

1544 

1545 if dialect.positional: 

1546 self.parameters = [dialect.execute_sequence_format()] 

1547 else: 

1548 self.parameters = [self._empty_dict_params] 

1549 

1550 return self 

1551 

1552 @classmethod 

1553 def _init_compiled( 

1554 cls, 

1555 dialect: Dialect, 

1556 connection: Connection, 

1557 dbapi_connection: PoolProxiedConnection, 

1558 execution_options: _ExecuteOptions, 

1559 compiled: SQLCompiler, 

1560 parameters: _CoreMultiExecuteParams, 

1561 invoked_statement: Executable, 

1562 extracted_parameters: Optional[Sequence[BindParameter[Any]]], 

1563 cache_hit: CacheStats = CacheStats.CACHING_DISABLED, 

1564 param_dict: _CoreSingleExecuteParams | None = None, 

1565 ) -> ExecutionContext: 

1566 """Initialize execution context for a Compiled construct.""" 

1567 

1568 self = cls.__new__(cls) 

1569 self.root_connection = connection 

1570 self._dbapi_connection = dbapi_connection 

1571 self.dialect = connection.dialect 

1572 self.extracted_parameters = extracted_parameters 

1573 self.invoked_statement = invoked_statement 

1574 self.compiled = compiled 

1575 self.cache_hit = cache_hit 

1576 

1577 self.execution_options = execution_options 

1578 

1579 self.result_column_struct = ( 

1580 compiled._result_columns, 

1581 compiled._ordered_columns, 

1582 compiled._textual_ordered_columns, 

1583 compiled._ad_hoc_textual, 

1584 compiled._loose_column_name_matching, 

1585 ) 

1586 

1587 self.isinsert = ii = compiled.isinsert 

1588 self.isupdate = iu = compiled.isupdate 

1589 self.isdelete = id_ = compiled.isdelete 

1590 self.is_text = compiled.isplaintext 

1591 

1592 if ii or iu or id_: 

1593 dml_statement = compiled.compile_state.statement # type: ignore[union-attr] # noqa: E501 

1594 if TYPE_CHECKING: 

1595 assert isinstance(dml_statement, UpdateBase) 

1596 self.is_crud = True 

1597 self._is_explicit_returning = ier = bool(dml_statement._returning) 

1598 self._is_implicit_returning = iir = bool( 

1599 compiled.implicit_returning 

1600 ) 

1601 if iir and dml_statement._supplemental_returning: 

1602 self._is_supplemental_returning = True 

1603 

1604 # dont mix implicit and explicit returning 

1605 assert not (iir and ier) 

1606 

1607 if (ier or iir) and compiled.for_executemany: 

1608 if ii and not self.dialect.insert_executemany_returning: 

1609 raise exc.InvalidRequestError( 

1610 f"Dialect {self.dialect.dialect_description} with " 

1611 f"current server capabilities does not support " 

1612 "INSERT..RETURNING when executemany is used" 

1613 ) 

1614 elif ( 

1615 ii 

1616 and dml_statement._sort_by_parameter_order 

1617 and not self.dialect.insert_executemany_returning_sort_by_parameter_order # noqa: E501 

1618 ): 

1619 raise exc.InvalidRequestError( 

1620 f"Dialect {self.dialect.dialect_description} with " 

1621 f"current server capabilities does not support " 

1622 "INSERT..RETURNING with deterministic row ordering " 

1623 "when executemany is used" 

1624 ) 

1625 elif ( 

1626 ii 

1627 and self.dialect.use_insertmanyvalues 

1628 and not compiled._insertmanyvalues 

1629 ): 

1630 raise exc.InvalidRequestError( 

1631 'Statement does not have "insertmanyvalues" ' 

1632 "enabled, can't use INSERT..RETURNING with " 

1633 "executemany in this case." 

1634 ) 

1635 elif iu and not self.dialect.update_executemany_returning: 

1636 raise exc.InvalidRequestError( 

1637 f"Dialect {self.dialect.dialect_description} with " 

1638 f"current server capabilities does not support " 

1639 "UPDATE..RETURNING when executemany is used" 

1640 ) 

1641 elif id_ and not self.dialect.delete_executemany_returning: 

1642 raise exc.InvalidRequestError( 

1643 f"Dialect {self.dialect.dialect_description} with " 

1644 f"current server capabilities does not support " 

1645 "DELETE..RETURNING when executemany is used" 

1646 ) 

1647 

1648 if not parameters: 

1649 self.compiled_parameters = [ 

1650 compiled.construct_params( 

1651 extracted_parameters=extracted_parameters, 

1652 escape_names=False, 

1653 _collected_params=param_dict, 

1654 ) 

1655 ] 

1656 else: 

1657 self.compiled_parameters = [ 

1658 compiled.construct_params( 

1659 m, 

1660 escape_names=False, 

1661 _group_number=grp, 

1662 extracted_parameters=extracted_parameters, 

1663 _collected_params=param_dict, 

1664 ) 

1665 for grp, m in enumerate(parameters) 

1666 ] 

1667 

1668 if len(parameters) > 1: 

1669 if self.isinsert and compiled._insertmanyvalues: 

1670 self.execute_style = ExecuteStyle.INSERTMANYVALUES 

1671 

1672 imv = compiled._insertmanyvalues 

1673 if imv.sentinel_columns is not None: 

1674 self._num_sentinel_cols = imv.num_sentinel_columns 

1675 else: 

1676 self.execute_style = ExecuteStyle.EXECUTEMANY 

1677 

1678 self.unicode_statement = compiled.string 

1679 

1680 self.cursor = self.create_cursor() 

1681 

1682 if self.compiled.insert_prefetch or self.compiled.update_prefetch: 

1683 self._process_execute_defaults() 

1684 

1685 processors = compiled._bind_processors 

1686 

1687 flattened_processors: Mapping[ 

1688 str, _BindProcessorType[Any] 

1689 ] = processors # type: ignore[assignment] 

1690 

1691 if compiled.literal_execute_params or compiled.post_compile_params: 

1692 if self.executemany: 

1693 raise exc.InvalidRequestError( 

1694 "'literal_execute' or 'expanding' parameters can't be " 

1695 "used with executemany()" 

1696 ) 

1697 

1698 expanded_state = compiled._process_parameters_for_postcompile( 

1699 self.compiled_parameters[0] 

1700 ) 

1701 

1702 # re-assign self.unicode_statement 

1703 self.unicode_statement = expanded_state.statement 

1704 

1705 self._expanded_parameters = expanded_state.parameter_expansion 

1706 

1707 flattened_processors = dict(processors) # type: ignore[arg-type] 

1708 flattened_processors.update(expanded_state.processors) 

1709 positiontup = expanded_state.positiontup 

1710 elif compiled.positional: 

1711 positiontup = self.compiled.positiontup 

1712 else: 

1713 positiontup = None 

1714 

1715 if compiled.schema_translate_map: 

1716 schema_translate_map = self.execution_options.get( 

1717 "schema_translate_map", {} 

1718 ) 

1719 rst = compiled.preparer._render_schema_translates 

1720 self.unicode_statement = rst( 

1721 self.unicode_statement, schema_translate_map 

1722 ) 

1723 

1724 # final self.unicode_statement is now assigned, encode if needed 

1725 # by dialect 

1726 self.statement = self.unicode_statement 

1727 

1728 # Convert the dictionary of bind parameter values 

1729 # into a dict or list to be sent to the DBAPI's 

1730 # execute() or executemany() method. 

1731 

1732 if compiled.positional: 

1733 core_positional_parameters: MutableSequence[Sequence[Any]] = [] 

1734 assert positiontup is not None 

1735 for compiled_params in self.compiled_parameters: 

1736 l_param: List[Any] = [ 

1737 ( 

1738 flattened_processors[key](compiled_params[key]) 

1739 if key in flattened_processors 

1740 else compiled_params[key] 

1741 ) 

1742 for key in positiontup 

1743 ] 

1744 core_positional_parameters.append( 

1745 dialect.execute_sequence_format(l_param) 

1746 ) 

1747 

1748 self.parameters = core_positional_parameters 

1749 else: 

1750 core_dict_parameters: MutableSequence[Dict[str, Any]] = [] 

1751 escaped_names = compiled.escaped_bind_names 

1752 

1753 # note that currently, "expanded" parameters will be present 

1754 # in self.compiled_parameters in their quoted form. This is 

1755 # slightly inconsistent with the approach taken as of 

1756 # #8056 where self.compiled_parameters is meant to contain unquoted 

1757 # param names. 

1758 d_param: Dict[str, Any] 

1759 for compiled_params in self.compiled_parameters: 

1760 if escaped_names: 

1761 d_param = { 

1762 escaped_names.get(key, key): ( 

1763 flattened_processors[key](compiled_params[key]) 

1764 if key in flattened_processors 

1765 else compiled_params[key] 

1766 ) 

1767 for key in compiled_params 

1768 } 

1769 else: 

1770 d_param = { 

1771 key: ( 

1772 flattened_processors[key](compiled_params[key]) 

1773 if key in flattened_processors 

1774 else compiled_params[key] 

1775 ) 

1776 for key in compiled_params 

1777 } 

1778 

1779 core_dict_parameters.append(d_param) 

1780 

1781 self.parameters = core_dict_parameters 

1782 

1783 return self 

1784 

1785 @classmethod 

1786 def _init_statement( 

1787 cls, 

1788 dialect: Dialect, 

1789 connection: Connection, 

1790 dbapi_connection: PoolProxiedConnection, 

1791 execution_options: _ExecuteOptions, 

1792 statement: str, 

1793 parameters: _DBAPIMultiExecuteParams, 

1794 ) -> ExecutionContext: 

1795 """Initialize execution context for a string SQL statement.""" 

1796 

1797 self = cls.__new__(cls) 

1798 self.root_connection = connection 

1799 self._dbapi_connection = dbapi_connection 

1800 self.dialect = connection.dialect 

1801 self.is_text = True 

1802 

1803 self.execution_options = execution_options 

1804 

1805 if not parameters: 

1806 if self.dialect.positional: 

1807 self.parameters = [dialect.execute_sequence_format()] 

1808 else: 

1809 self.parameters = [self._empty_dict_params] 

1810 elif isinstance(parameters[0], dialect.execute_sequence_format): 

1811 self.parameters = parameters 

1812 elif isinstance(parameters[0], dict): 

1813 self.parameters = parameters 

1814 else: 

1815 self.parameters = [ 

1816 dialect.execute_sequence_format(p) for p in parameters 

1817 ] 

1818 

1819 if len(parameters) > 1: 

1820 self.execute_style = ExecuteStyle.EXECUTEMANY 

1821 

1822 self.statement = self.unicode_statement = statement 

1823 

1824 self.cursor = self.create_cursor() 

1825 return self 

1826 

1827 @classmethod 

1828 def _init_default( 

1829 cls, 

1830 dialect: Dialect, 

1831 connection: Connection, 

1832 dbapi_connection: PoolProxiedConnection, 

1833 execution_options: _ExecuteOptions, 

1834 ) -> ExecutionContext: 

1835 """Initialize execution context for a ColumnDefault construct.""" 

1836 

1837 self = cls.__new__(cls) 

1838 self.root_connection = connection 

1839 self._dbapi_connection = dbapi_connection 

1840 self.dialect = connection.dialect 

1841 

1842 self.execution_options = execution_options 

1843 

1844 self.cursor = self.create_cursor() 

1845 return self 

1846 

1847 def _get_cache_stats(self) -> str: 

1848 if self.compiled is None: 

1849 return "raw sql" 

1850 

1851 now = perf_counter() 

1852 

1853 ch = self.cache_hit 

1854 

1855 gen_time = self.compiled._gen_time 

1856 assert gen_time is not None 

1857 

1858 if ch is NO_CACHE_KEY: 

1859 return "no key %.5fs" % (now - gen_time,) 

1860 elif ch is CACHE_HIT: 

1861 return "cached since %.4gs ago" % (now - gen_time,) 

1862 elif ch is CACHE_MISS: 

1863 return "generated in %.5fs" % (now - gen_time,) 

1864 elif ch is CACHING_DISABLED: 

1865 if "_cache_disable_reason" in self.execution_options: 

1866 return "caching disabled (%s) %.5fs " % ( 

1867 self.execution_options["_cache_disable_reason"], 

1868 now - gen_time, 

1869 ) 

1870 else: 

1871 return "caching disabled %.5fs" % (now - gen_time,) 

1872 elif ch is NO_DIALECT_SUPPORT: 

1873 return "dialect %s+%s does not support caching %.5fs" % ( 

1874 self.dialect.name, 

1875 self.dialect.driver, 

1876 now - gen_time, 

1877 ) 

1878 else: 

1879 return "unknown" 

1880 

1881 @property 

1882 def executemany(self): # type: ignore[override] 

1883 return self.execute_style in ( 

1884 ExecuteStyle.EXECUTEMANY, 

1885 ExecuteStyle.INSERTMANYVALUES, 

1886 ) 

1887 

1888 @util.memoized_property 

1889 def identifier_preparer(self): 

1890 if self.compiled: 

1891 return self.compiled.preparer 

1892 elif "schema_translate_map" in self.execution_options: 

1893 return self.dialect.identifier_preparer._with_schema_translate( 

1894 self.execution_options["schema_translate_map"] 

1895 ) 

1896 else: 

1897 return self.dialect.identifier_preparer 

1898 

1899 @util.memoized_property 

1900 def engine(self): 

1901 return self.root_connection.engine 

1902 

1903 @util.memoized_property 

1904 def postfetch_cols(self) -> Optional[Sequence[Column[Any]]]: 

1905 if TYPE_CHECKING: 

1906 assert isinstance(self.compiled, SQLCompiler) 

1907 return self.compiled.postfetch 

1908 

1909 @util.memoized_property 

1910 def prefetch_cols(self) -> Optional[Sequence[Column[Any]]]: 

1911 if TYPE_CHECKING: 

1912 assert isinstance(self.compiled, SQLCompiler) 

1913 if self.isinsert: 

1914 return self.compiled.insert_prefetch 

1915 elif self.isupdate: 

1916 return self.compiled.update_prefetch 

1917 else: 

1918 return () 

1919 

1920 @util.memoized_property 

1921 def no_parameters(self): 

1922 return self.execution_options.get("no_parameters", False) 

1923 

1924 def _execute_scalar( 

1925 self, 

1926 stmt: str, 

1927 type_: Optional[TypeEngine[Any]], 

1928 parameters: Optional[_DBAPISingleExecuteParams] = None, 

1929 ) -> Any: 

1930 """Execute a string statement on the current cursor, returning a 

1931 scalar result. 

1932 

1933 Used to fire off sequences, default phrases, and "select lastrowid" 

1934 types of statements individually or in the context of a parent INSERT 

1935 or UPDATE statement. 

1936 

1937 """ 

1938 

1939 conn = self.root_connection 

1940 

1941 if "schema_translate_map" in self.execution_options: 

1942 schema_translate_map = self.execution_options.get( 

1943 "schema_translate_map", {} 

1944 ) 

1945 

1946 rst = self.identifier_preparer._render_schema_translates 

1947 stmt = rst(stmt, schema_translate_map) 

1948 

1949 if not parameters: 

1950 if self.dialect.positional: 

1951 parameters = self.dialect.execute_sequence_format() 

1952 else: 

1953 parameters = {} 

1954 

1955 conn._cursor_execute(self.cursor, stmt, parameters, context=self) 

1956 row = self.cursor.fetchone() 

1957 if row is not None: 

1958 r = row[0] 

1959 else: 

1960 r = None 

1961 if type_ is not None: 

1962 # apply type post processors to the result 

1963 proc = type_._cached_result_processor( 

1964 self.dialect, self.cursor.description[0][1] 

1965 ) 

1966 if proc: 

1967 return proc(r) 

1968 return r 

1969 

1970 @util.memoized_property 

1971 def connection(self): 

1972 return self.root_connection 

1973 

1974 def _use_server_side_cursor(self): 

1975 if not self.dialect.supports_server_side_cursors: 

1976 return False 

1977 

1978 if self.dialect.server_side_cursors: 

1979 # this is deprecated 

1980 use_server_side = self.execution_options.get( 

1981 "stream_results", True 

1982 ) and ( 

1983 self.compiled 

1984 and isinstance(self.compiled.statement, expression.Selectable) 

1985 or ( 

1986 ( 

1987 not self.compiled 

1988 or isinstance( 

1989 self.compiled.statement, expression.TextClause 

1990 ) 

1991 ) 

1992 and self.unicode_statement 

1993 and SERVER_SIDE_CURSOR_RE.match(self.unicode_statement) 

1994 ) 

1995 ) 

1996 else: 

1997 use_server_side = self.execution_options.get( 

1998 "stream_results", False 

1999 ) 

2000 

2001 return use_server_side 

2002 

2003 def create_cursor(self) -> DBAPICursor: 

2004 if ( 

2005 # inlining initial preference checks for SS cursors 

2006 self.dialect.supports_server_side_cursors 

2007 and ( 

2008 self.execution_options.get("stream_results", False) 

2009 or ( 

2010 self.dialect.server_side_cursors 

2011 and self._use_server_side_cursor() 

2012 ) 

2013 ) 

2014 ): 

2015 self._is_server_side = True 

2016 return self.create_server_side_cursor() 

2017 else: 

2018 self._is_server_side = False 

2019 return self.create_default_cursor() 

2020 

2021 def fetchall_for_returning(self, cursor): 

2022 return cursor.fetchall() 

2023 

2024 def create_default_cursor(self) -> DBAPICursor: 

2025 return self._dbapi_connection.cursor() 

2026 

2027 def create_server_side_cursor(self) -> DBAPICursor: 

2028 raise NotImplementedError() 

2029 

2030 def pre_exec(self): 

2031 pass 

2032 

2033 def get_out_parameter_values(self, names): 

2034 raise NotImplementedError( 

2035 "This dialect does not support OUT parameters" 

2036 ) 

2037 

2038 def post_exec(self): 

2039 pass 

2040 

2041 def get_result_processor( 

2042 self, type_: TypeEngine[Any], colname: str, coltype: DBAPIType 

2043 ) -> Optional[_ResultProcessorType[Any]]: 

2044 """Return a 'result processor' for a given type as present in 

2045 cursor.description. 

2046 

2047 This has a default implementation that dialects can override 

2048 for context-sensitive result type handling. 

2049 

2050 """ 

2051 return type_._cached_result_processor(self.dialect, coltype) 

2052 

2053 def get_lastrowid(self) -> int: 

2054 """return self.cursor.lastrowid, or equivalent, after an INSERT. 

2055 

2056 This may involve calling special cursor functions, issuing a new SELECT 

2057 on the cursor (or a new one), or returning a stored value that was 

2058 calculated within post_exec(). 

2059 

2060 This function will only be called for dialects which support "implicit" 

2061 primary key generation, keep preexecute_autoincrement_sequences set to 

2062 False, and when no explicit id value was bound to the statement. 

2063 

2064 The function is called once for an INSERT statement that would need to 

2065 return the last inserted primary key for those dialects that make use 

2066 of the lastrowid concept. In these cases, it is called directly after 

2067 :meth:`.ExecutionContext.post_exec`. 

2068 

2069 """ 

2070 return self.cursor.lastrowid 

2071 

2072 def handle_dbapi_exception(self, e): 

2073 pass 

2074 

2075 @util.non_memoized_property 

2076 def rowcount(self) -> int: 

2077 if self._rowcount is not None: 

2078 return self._rowcount 

2079 else: 

2080 return self.cursor.rowcount 

2081 

2082 @property 

2083 def _has_rowcount(self): 

2084 return self._rowcount is not None 

2085 

2086 def supports_sane_rowcount(self): 

2087 return self.dialect.supports_sane_rowcount 

2088 

2089 def supports_sane_multi_rowcount(self): 

2090 return self.dialect.supports_sane_multi_rowcount 

2091 

2092 def _setup_result_proxy(self): 

2093 exec_opt = self.execution_options 

2094 

2095 if self._rowcount is None and exec_opt.get("preserve_rowcount", False): 

2096 self._rowcount = self.cursor.rowcount 

2097 

2098 yp: Optional[Union[int, bool]] 

2099 if self.is_crud or self.is_text: 

2100 result = self._setup_dml_or_text_result() 

2101 yp = False 

2102 else: 

2103 yp = exec_opt.get("yield_per", None) 

2104 sr = self._is_server_side or exec_opt.get("stream_results", False) 

2105 strategy = self.cursor_fetch_strategy 

2106 if sr and strategy is _cursor._DEFAULT_FETCH: 

2107 strategy = _cursor.BufferedRowCursorFetchStrategy( 

2108 self.cursor, self.execution_options 

2109 ) 

2110 cursor_description: _DBAPICursorDescription = ( 

2111 strategy.alternate_cursor_description 

2112 or self.cursor.description 

2113 ) 

2114 if cursor_description is None: 

2115 strategy = _cursor._NO_CURSOR_DQL 

2116 

2117 result = _cursor.CursorResult(self, strategy, cursor_description) 

2118 

2119 compiled = self.compiled 

2120 

2121 if ( 

2122 compiled 

2123 and not self.isddl 

2124 and cast(SQLCompiler, compiled).has_out_parameters 

2125 ): 

2126 self._setup_out_parameters(result) 

2127 

2128 self._soft_closed = result._soft_closed 

2129 

2130 if yp: 

2131 result = result.yield_per(yp) 

2132 

2133 return result 

2134 

2135 def _setup_out_parameters(self, result): 

2136 compiled = cast(SQLCompiler, self.compiled) 

2137 

2138 out_bindparams = [ 

2139 (param, name) 

2140 for param, name in compiled.bind_names.items() 

2141 if param.isoutparam 

2142 ] 

2143 out_parameters = {} 

2144 

2145 for bindparam, raw_value in zip( 

2146 [param for param, name in out_bindparams], 

2147 self.get_out_parameter_values( 

2148 [name for param, name in out_bindparams] 

2149 ), 

2150 ): 

2151 type_ = bindparam.type 

2152 impl_type = type_.dialect_impl(self.dialect) 

2153 dbapi_type = impl_type.get_dbapi_type(self.dialect.loaded_dbapi) 

2154 result_processor = impl_type.result_processor( 

2155 self.dialect, dbapi_type 

2156 ) 

2157 if result_processor is not None: 

2158 raw_value = result_processor(raw_value) 

2159 out_parameters[bindparam.key] = raw_value 

2160 

2161 result.out_parameters = out_parameters 

2162 

2163 def _setup_dml_or_text_result(self): 

2164 compiled = cast(SQLCompiler, self.compiled) 

2165 

2166 strategy: ResultFetchStrategy = self.cursor_fetch_strategy 

2167 

2168 if self.isinsert: 

2169 if ( 

2170 self.execute_style is ExecuteStyle.INSERTMANYVALUES 

2171 and compiled.effective_returning 

2172 ): 

2173 strategy = _cursor.FullyBufferedCursorFetchStrategy( 

2174 self.cursor, 

2175 initial_buffer=self._insertmanyvalues_rows, 

2176 # maintain alt cursor description if set by the 

2177 # dialect, e.g. mssql preserves it 

2178 alternate_description=( 

2179 strategy.alternate_cursor_description 

2180 ), 

2181 ) 

2182 

2183 if compiled.postfetch_lastrowid: 

2184 self.inserted_primary_key_rows = ( 

2185 self._setup_ins_pk_from_lastrowid() 

2186 ) 

2187 # else if not self._is_implicit_returning, 

2188 # the default inserted_primary_key_rows accessor will 

2189 # return an "empty" primary key collection when accessed. 

2190 

2191 if self._is_server_side and strategy is _cursor._DEFAULT_FETCH: 

2192 strategy = _cursor.BufferedRowCursorFetchStrategy( 

2193 self.cursor, self.execution_options 

2194 ) 

2195 

2196 if strategy is _cursor._NO_CURSOR_DML: 

2197 cursor_description = None 

2198 else: 

2199 cursor_description = ( 

2200 strategy.alternate_cursor_description 

2201 or self.cursor.description 

2202 ) 

2203 

2204 if cursor_description is None: 

2205 strategy = _cursor._NO_CURSOR_DML 

2206 elif self._num_sentinel_cols: 

2207 assert self.execute_style is ExecuteStyle.INSERTMANYVALUES 

2208 # the sentinel columns are handled in CursorResult._init_metadata 

2209 # using essentially _reduce 

2210 

2211 result: _cursor.CursorResult[Any] = _cursor.CursorResult( 

2212 self, strategy, cursor_description 

2213 ) 

2214 

2215 if self.isinsert: 

2216 if self._is_implicit_returning: 

2217 rows = result.all() 

2218 

2219 self.returned_default_rows = rows 

2220 

2221 self.inserted_primary_key_rows = ( 

2222 self._setup_ins_pk_from_implicit_returning(result, rows) 

2223 ) 

2224 

2225 # test that it has a cursor metadata that is accurate. the 

2226 # first row will have been fetched and current assumptions 

2227 # are that the result has only one row, until executemany() 

2228 # support is added here. 

2229 assert result._metadata.returns_rows 

2230 

2231 # Insert statement has both return_defaults() and 

2232 # returning(). rewind the result on the list of rows 

2233 # we just used. 

2234 if self._is_supplemental_returning: 

2235 result._rewind(rows) 

2236 else: 

2237 result._soft_close() 

2238 elif not self._is_explicit_returning: 

2239 result._soft_close() 

2240 

2241 # we assume here the result does not return any rows. 

2242 # *usually*, this will be true. However, some dialects 

2243 # such as that of MSSQL/pyodbc need to SELECT a post fetch 

2244 # function so this is not necessarily true. 

2245 # assert not result.returns_rows 

2246 

2247 elif self._is_implicit_returning: 

2248 rows = result.all() 

2249 

2250 if rows: 

2251 self.returned_default_rows = rows 

2252 self._rowcount = len(rows) 

2253 

2254 if self._is_supplemental_returning: 

2255 result._rewind(rows) 

2256 else: 

2257 result._soft_close() 

2258 

2259 # test that it has a cursor metadata that is accurate. 

2260 # the rows have all been fetched however. 

2261 assert result._metadata.returns_rows 

2262 

2263 elif not result._metadata.returns_rows: 

2264 # no results, get rowcount 

2265 # (which requires open cursor on some drivers) 

2266 if self._rowcount is None: 

2267 self._rowcount = self.cursor.rowcount 

2268 result._soft_close() 

2269 elif self.isupdate or self.isdelete: 

2270 if self._rowcount is None: 

2271 self._rowcount = self.cursor.rowcount 

2272 return result 

2273 

2274 @util.memoized_property 

2275 def inserted_primary_key_rows(self): 

2276 # if no specific "get primary key" strategy was set up 

2277 # during execution, return a "default" primary key based 

2278 # on what's in the compiled_parameters and nothing else. 

2279 return self._setup_ins_pk_from_empty() 

2280 

2281 def _setup_ins_pk_from_lastrowid(self): 

2282 getter = cast( 

2283 SQLCompiler, self.compiled 

2284 )._inserted_primary_key_from_lastrowid_getter 

2285 lastrowid = self.get_lastrowid() 

2286 return [getter(lastrowid, self.compiled_parameters[0])] 

2287 

2288 def _setup_ins_pk_from_empty(self): 

2289 getter = cast( 

2290 SQLCompiler, self.compiled 

2291 )._inserted_primary_key_from_lastrowid_getter 

2292 return [getter(None, param) for param in self.compiled_parameters] 

2293 

2294 def _setup_ins_pk_from_implicit_returning(self, result, rows): 

2295 if not rows: 

2296 return [] 

2297 

2298 getter = cast( 

2299 SQLCompiler, self.compiled 

2300 )._inserted_primary_key_from_returning_getter 

2301 compiled_params = self.compiled_parameters 

2302 

2303 return [ 

2304 getter(row, param) for row, param in zip(rows, compiled_params) 

2305 ] 

2306 

2307 def lastrow_has_defaults(self) -> bool: 

2308 return (self.isinsert or self.isupdate) and bool( 

2309 cast(SQLCompiler, self.compiled).postfetch 

2310 ) 

2311 

2312 def _prepare_set_input_sizes( 

2313 self, 

2314 ) -> Optional[List[Tuple[str, Any, TypeEngine[Any]]]]: 

2315 """Given a cursor and ClauseParameters, prepare arguments 

2316 in order to call the appropriate 

2317 style of ``setinputsizes()`` on the cursor, using DB-API types 

2318 from the bind parameter's ``TypeEngine`` objects. 

2319 

2320 This method only called by those dialects which set the 

2321 :attr:`.Dialect.bind_typing` attribute to 

2322 :attr:`.BindTyping.SETINPUTSIZES`. Python-oracledb and cx_Oracle are 

2323 the only DBAPIs that requires setinputsizes(); pyodbc offers it as an 

2324 option. 

2325 

2326 Prior to SQLAlchemy 2.0, the setinputsizes() approach was also used 

2327 for pg8000 and asyncpg, which has been changed to inline rendering 

2328 of casts. 

2329 

2330 """ 

2331 if self.isddl or self.is_text: 

2332 return None 

2333 

2334 compiled = cast(SQLCompiler, self.compiled) 

2335 

2336 inputsizes = compiled._get_set_input_sizes_lookup() 

2337 

2338 if inputsizes is None: 

2339 return None 

2340 

2341 dialect = self.dialect 

2342 

2343 # all of the rest of this... cython? 

2344 

2345 if dialect._has_events: 

2346 inputsizes = dict(inputsizes) 

2347 dialect.dispatch.do_setinputsizes( 

2348 inputsizes, self.cursor, self.statement, self.parameters, self 

2349 ) 

2350 

2351 if compiled.escaped_bind_names: 

2352 escaped_bind_names = compiled.escaped_bind_names 

2353 else: 

2354 escaped_bind_names = None 

2355 

2356 if dialect.positional: 

2357 items = [ 

2358 (key, compiled.binds[key]) 

2359 for key in compiled.positiontup or () 

2360 ] 

2361 else: 

2362 items = [ 

2363 (key, bindparam) 

2364 for bindparam, key in compiled.bind_names.items() 

2365 ] 

2366 

2367 generic_inputsizes: List[Tuple[str, Any, TypeEngine[Any]]] = [] 

2368 for key, bindparam in items: 

2369 if bindparam in compiled.literal_execute_params: 

2370 continue 

2371 

2372 if key in self._expanded_parameters: 

2373 if is_tuple_type(bindparam.type): 

2374 num = len(bindparam.type.types) 

2375 dbtypes = inputsizes[bindparam] 

2376 generic_inputsizes.extend( 

2377 ( 

2378 ( 

2379 escaped_bind_names.get(paramname, paramname) 

2380 if escaped_bind_names is not None 

2381 else paramname 

2382 ), 

2383 dbtypes[idx % num], 

2384 bindparam.type.types[idx % num], 

2385 ) 

2386 for idx, paramname in enumerate( 

2387 self._expanded_parameters[key] 

2388 ) 

2389 ) 

2390 else: 

2391 dbtype = inputsizes.get(bindparam, None) 

2392 generic_inputsizes.extend( 

2393 ( 

2394 ( 

2395 escaped_bind_names.get(paramname, paramname) 

2396 if escaped_bind_names is not None 

2397 else paramname 

2398 ), 

2399 dbtype, 

2400 bindparam.type, 

2401 ) 

2402 for paramname in self._expanded_parameters[key] 

2403 ) 

2404 else: 

2405 dbtype = inputsizes.get(bindparam, None) 

2406 

2407 escaped_name = ( 

2408 escaped_bind_names.get(key, key) 

2409 if escaped_bind_names is not None 

2410 else key 

2411 ) 

2412 

2413 generic_inputsizes.append( 

2414 (escaped_name, dbtype, bindparam.type) 

2415 ) 

2416 

2417 return generic_inputsizes 

2418 

2419 def _exec_default(self, column, default, type_): 

2420 if default.is_sequence: 

2421 return self.fire_sequence(default, type_) 

2422 elif default.is_callable: 

2423 # this codepath is not normally used as it's inlined 

2424 # into _process_execute_defaults 

2425 self.current_column = column 

2426 return default.arg(self) 

2427 elif default.is_clause_element: 

2428 return self._exec_default_clause_element(column, default, type_) 

2429 else: 

2430 # this codepath is not normally used as it's inlined 

2431 # into _process_execute_defaults 

2432 return default.arg 

2433 

2434 def _exec_default_clause_element(self, column, default, type_): 

2435 # execute a default that's a complete clause element. Here, we have 

2436 # to re-implement a miniature version of the compile->parameters-> 

2437 # cursor.execute() sequence, since we don't want to modify the state 

2438 # of the connection / result in progress or create new connection/ 

2439 # result objects etc. 

2440 # .. versionchanged:: 1.4 

2441 

2442 if not default._arg_is_typed: 

2443 default_arg = expression.type_coerce(default.arg, type_) 

2444 else: 

2445 default_arg = default.arg 

2446 compiled = expression.select(default_arg).compile(dialect=self.dialect) 

2447 compiled_params = compiled.construct_params() 

2448 processors = compiled._bind_processors 

2449 if compiled.positional: 

2450 parameters = self.dialect.execute_sequence_format( 

2451 [ 

2452 ( 

2453 processors[key](compiled_params[key]) # type: ignore[operator] # noqa: E501 

2454 if key in processors 

2455 else compiled_params[key] 

2456 ) 

2457 for key in compiled.positiontup or () 

2458 ] 

2459 ) 

2460 else: 

2461 parameters = { 

2462 key: ( 

2463 processors[key](compiled_params[key]) # type: ignore[assignment, operator] # noqa: E501 

2464 if key in processors 

2465 else compiled_params[key] 

2466 ) 

2467 for key in compiled_params 

2468 } 

2469 return self._execute_scalar( 

2470 str(compiled), type_, parameters=parameters 

2471 ) 

2472 

2473 current_parameters: Optional[_CoreSingleExecuteParams] = None 

2474 """A dictionary of parameters applied to the current row. 

2475 

2476 This attribute is only available in the context of a user-defined default 

2477 generation function, e.g. as described at :ref:`context_default_functions`. 

2478 It consists of a dictionary which includes entries for each column/value 

2479 pair that is to be part of the INSERT or UPDATE statement. The keys of the 

2480 dictionary will be the key value of each :class:`_schema.Column`, 

2481 which is usually 

2482 synonymous with the name. 

2483 

2484 Note that the :attr:`.DefaultExecutionContext.current_parameters` attribute 

2485 does not accommodate for the "multi-values" feature of the 

2486 :meth:`_expression.Insert.values` method. The 

2487 :meth:`.DefaultExecutionContext.get_current_parameters` method should be 

2488 preferred. 

2489 

2490 .. seealso:: 

2491 

2492 :meth:`.DefaultExecutionContext.get_current_parameters` 

2493 

2494 :ref:`context_default_functions` 

2495 

2496 """ 

2497 

2498 def get_current_parameters(self, isolate_multiinsert_groups=True): 

2499 """Return a dictionary of parameters applied to the current row. 

2500 

2501 This method can only be used in the context of a user-defined default 

2502 generation function, e.g. as described at 

2503 :ref:`context_default_functions`. When invoked, a dictionary is 

2504 returned which includes entries for each column/value pair that is part 

2505 of the INSERT or UPDATE statement. The keys of the dictionary will be 

2506 the key value of each :class:`_schema.Column`, 

2507 which is usually synonymous 

2508 with the name. 

2509 

2510 :param isolate_multiinsert_groups=True: indicates that multi-valued 

2511 INSERT constructs created using :meth:`_expression.Insert.values` 

2512 should be 

2513 handled by returning only the subset of parameters that are local 

2514 to the current column default invocation. When ``False``, the 

2515 raw parameters of the statement are returned including the 

2516 naming convention used in the case of multi-valued INSERT. 

2517 

2518 .. seealso:: 

2519 

2520 :attr:`.DefaultExecutionContext.current_parameters` 

2521 

2522 :ref:`context_default_functions` 

2523 

2524 """ 

2525 try: 

2526 parameters = self.current_parameters 

2527 column = self.current_column 

2528 except AttributeError: 

2529 raise exc.InvalidRequestError( 

2530 "get_current_parameters() can only be invoked in the " 

2531 "context of a Python side column default function" 

2532 ) 

2533 else: 

2534 assert column is not None 

2535 assert parameters is not None 

2536 compile_state = cast( 

2537 "DMLState", cast(SQLCompiler, self.compiled).compile_state 

2538 ) 

2539 assert compile_state is not None 

2540 if ( 

2541 isolate_multiinsert_groups 

2542 and dml.isinsert(compile_state) 

2543 and compile_state._has_multi_parameters 

2544 ): 

2545 if column._is_multiparam_column: 

2546 index = column.index + 1 

2547 d = {column.original.key: parameters[column.key]} 

2548 else: 

2549 d = {column.key: parameters[column.key]} 

2550 index = 0 

2551 assert compile_state._dict_parameters is not None 

2552 keys = compile_state._dict_parameters.keys() 

2553 d.update( 

2554 (key, parameters["%s_m%d" % (key, index)]) for key in keys 

2555 ) 

2556 return d 

2557 else: 

2558 return parameters 

2559 

2560 def get_insert_default(self, column): 

2561 if column.default is None: 

2562 return None 

2563 else: 

2564 return self._exec_default(column, column.default, column.type) 

2565 

2566 def get_update_default(self, column): 

2567 if column.onupdate is None: 

2568 return None 

2569 else: 

2570 return self._exec_default(column, column.onupdate, column.type) 

2571 

2572 def _process_execute_defaults(self): 

2573 compiled = cast(SQLCompiler, self.compiled) 

2574 

2575 key_getter = compiled._within_exec_param_key_getter 

2576 

2577 sentinel_counter = 0 

2578 

2579 if compiled.insert_prefetch: 

2580 prefetch_recs = [ 

2581 ( 

2582 c, 

2583 key_getter(c), 

2584 c._default_description_tuple, 

2585 self.get_insert_default, 

2586 ) 

2587 for c in compiled.insert_prefetch 

2588 ] 

2589 elif compiled.update_prefetch: 

2590 prefetch_recs = [ 

2591 ( 

2592 c, 

2593 key_getter(c), 

2594 c._onupdate_description_tuple, 

2595 self.get_update_default, 

2596 ) 

2597 for c in compiled.update_prefetch 

2598 ] 

2599 else: 

2600 prefetch_recs = [] 

2601 

2602 for param in self.compiled_parameters: 

2603 self.current_parameters = param 

2604 

2605 for ( 

2606 c, 

2607 param_key, 

2608 (arg, is_scalar, is_callable, is_sentinel), 

2609 fallback, 

2610 ) in prefetch_recs: 

2611 if is_sentinel: 

2612 param[param_key] = sentinel_counter 

2613 sentinel_counter += 1 

2614 elif is_scalar: 

2615 param[param_key] = arg 

2616 elif is_callable: 

2617 self.current_column = c 

2618 param[param_key] = arg(self) 

2619 else: 

2620 val = fallback(c) 

2621 if val is not None: 

2622 param[param_key] = val 

2623 

2624 del self.current_parameters 

2625 

2626 

2627DefaultDialect.execution_ctx_cls = DefaultExecutionContext