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

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

819 statements  

1# engine/result.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 

8"""Define generic result set constructs.""" 

9 

10from __future__ import annotations 

11 

12from enum import Enum 

13import functools 

14import itertools 

15import operator 

16import typing 

17from typing import Any 

18from typing import Callable 

19from typing import cast 

20from typing import Dict 

21from typing import Generic 

22from typing import Iterable 

23from typing import Iterator 

24from typing import List 

25from typing import Mapping 

26from typing import NoReturn 

27from typing import Optional 

28from typing import overload 

29from typing import Sequence 

30from typing import Set 

31from typing import Tuple 

32from typing import TYPE_CHECKING 

33from typing import TypeVar 

34from typing import Union 

35 

36from ._util_cy import tuplegetter as tuplegetter 

37from .row import Row 

38from .row import RowMapping 

39from .. import exc 

40from .. import util 

41from ..sql.base import _generative 

42from ..sql.base import HasMemoized 

43from ..sql.base import InPlaceGenerative 

44from ..util import deprecated 

45from ..util import HasMemoized_ro_memoized_attribute 

46from ..util import NONE_SET 

47from ..util.typing import Literal 

48from ..util.typing import Self 

49from ..util.typing import TupleAny 

50from ..util.typing import TypeVarTuple 

51from ..util.typing import Unpack 

52 

53if typing.TYPE_CHECKING: 

54 from ..sql.elements import SQLCoreOperations 

55 from ..sql.type_api import _ResultProcessorType 

56 

57_KeyType = Union[str, "SQLCoreOperations[Any]"] 

58_KeyIndexType = Union[_KeyType, int] 

59 

60# is overridden in cursor using _CursorKeyMapRecType 

61_KeyMapRecType = Any 

62 

63_KeyMapType = Mapping[_KeyType, _KeyMapRecType] 

64 

65 

66_RowData = Union[Row[Unpack[TupleAny]], RowMapping, Any] 

67"""A generic form of "row" that accommodates for the different kinds of 

68"rows" that different result objects return, including row, row mapping, and 

69scalar values""" 

70 

71 

72_R = TypeVar("_R", bound=_RowData) 

73_T = TypeVar("_T", bound=Any) 

74_Ts = TypeVarTuple("_Ts") 

75 

76_InterimRowType = Union[_R, TupleAny] 

77"""a catchall "anything" kind of return type that can be applied 

78across all the result types 

79 

80""" 

81 

82_InterimSupportsScalarsRowType = Union[Row[Unpack[TupleAny]], Any] 

83 

84_ProcessorsType = Sequence[Optional["_ResultProcessorType[Any]"]] 

85_TupleGetterType = Callable[[Sequence[Any]], Sequence[Any]] 

86_UniqueFilterType = Callable[[Any], Any] 

87_UniqueFilterStateType = Tuple[Set[Any], Optional[_UniqueFilterType]] 

88 

89 

90class ResultMetaData: 

91 """Base for metadata about result rows.""" 

92 

93 __slots__ = () 

94 

95 _tuplefilter: Optional[_TupleGetterType] = None 

96 _translated_indexes: Optional[Sequence[int]] = None 

97 _unique_filters: Optional[Sequence[Callable[[Any], Any]]] = None 

98 _keymap: _KeyMapType 

99 _keys: Sequence[str] 

100 _processors: Optional[_ProcessorsType] 

101 _key_to_index: Dict[_KeyType, int] 

102 

103 @property 

104 def keys(self) -> RMKeyView: 

105 return RMKeyView(self) 

106 

107 def _has_key(self, key: object) -> bool: 

108 raise NotImplementedError() 

109 

110 def _for_freeze(self) -> ResultMetaData: 

111 raise NotImplementedError() 

112 

113 @overload 

114 def _key_fallback( 

115 self, key: Any, err: Optional[Exception], raiseerr: Literal[True] = ... 

116 ) -> NoReturn: ... 

117 

118 @overload 

119 def _key_fallback( 

120 self, 

121 key: Any, 

122 err: Optional[Exception], 

123 raiseerr: Literal[False] = ..., 

124 ) -> None: ... 

125 

126 @overload 

127 def _key_fallback( 

128 self, key: Any, err: Optional[Exception], raiseerr: bool = ... 

129 ) -> Optional[NoReturn]: ... 

130 

131 def _key_fallback( 

132 self, key: Any, err: Optional[Exception], raiseerr: bool = True 

133 ) -> Optional[NoReturn]: 

134 assert raiseerr 

135 raise KeyError(key) from err 

136 

137 def _raise_for_ambiguous_column_name( 

138 self, rec: _KeyMapRecType 

139 ) -> NoReturn: 

140 raise NotImplementedError( 

141 "ambiguous column name logic is implemented for " 

142 "CursorResultMetaData" 

143 ) 

144 

145 def _index_for_key( 

146 self, key: _KeyIndexType, raiseerr: bool 

147 ) -> Optional[int]: 

148 raise NotImplementedError() 

149 

150 def _indexes_for_keys( 

151 self, keys: Sequence[_KeyIndexType] 

152 ) -> Sequence[int]: 

153 raise NotImplementedError() 

154 

155 def _metadata_for_keys( 

156 self, keys: Sequence[_KeyIndexType] 

157 ) -> Iterator[_KeyMapRecType]: 

158 raise NotImplementedError() 

159 

160 def _reduce(self, keys: Sequence[_KeyIndexType]) -> ResultMetaData: 

161 raise NotImplementedError() 

162 

163 def _getter( 

164 self, key: Any, raiseerr: bool = True 

165 ) -> Optional[Callable[[Row[Unpack[TupleAny]]], Any]]: 

166 index = self._index_for_key(key, raiseerr) 

167 

168 if index is not None: 

169 return operator.itemgetter(index) 

170 else: 

171 return None 

172 

173 def _row_as_tuple_getter( 

174 self, keys: Sequence[_KeyIndexType] 

175 ) -> _TupleGetterType: 

176 indexes = self._indexes_for_keys(keys) 

177 return tuplegetter(*indexes) 

178 

179 def _make_key_to_index( 

180 self, keymap: Mapping[_KeyType, Sequence[Any]], index: int 

181 ) -> Dict[_KeyType, int]: 

182 return { 

183 key: rec[index] 

184 for key, rec in keymap.items() 

185 if rec[index] is not None 

186 } 

187 

188 def _key_not_found(self, key: Any, attr_error: bool) -> NoReturn: 

189 if key in self._keymap: 

190 # the index must be none in this case 

191 self._raise_for_ambiguous_column_name(self._keymap[key]) 

192 else: 

193 # unknown key 

194 if attr_error: 

195 try: 

196 self._key_fallback(key, None) 

197 except KeyError as ke: 

198 raise AttributeError(ke.args[0]) from ke 

199 else: 

200 self._key_fallback(key, None) 

201 

202 @property 

203 def _effective_processors(self) -> Optional[_ProcessorsType]: 

204 if not self._processors or NONE_SET.issuperset(self._processors): 

205 return None 

206 else: 

207 return self._processors 

208 

209 

210class RMKeyView(typing.KeysView[Any]): 

211 __slots__ = ("_parent", "_keys") 

212 

213 _parent: ResultMetaData 

214 _keys: Sequence[str] 

215 

216 def __init__(self, parent: ResultMetaData): 

217 self._parent = parent 

218 self._keys = [k for k in parent._keys if k is not None] 

219 

220 def __len__(self) -> int: 

221 return len(self._keys) 

222 

223 def __repr__(self) -> str: 

224 return "{0.__class__.__name__}({0._keys!r})".format(self) 

225 

226 def __iter__(self) -> Iterator[str]: 

227 return iter(self._keys) 

228 

229 def __contains__(self, item: Any) -> bool: 

230 if isinstance(item, int): 

231 return False 

232 

233 # note this also includes special key fallback behaviors 

234 # which also don't seem to be tested in test_resultset right now 

235 return self._parent._has_key(item) 

236 

237 def __eq__(self, other: Any) -> bool: 

238 return list(other) == list(self) 

239 

240 def __ne__(self, other: Any) -> bool: 

241 return list(other) != list(self) 

242 

243 

244class SimpleResultMetaData(ResultMetaData): 

245 """result metadata for in-memory collections.""" 

246 

247 __slots__ = ( 

248 "_keys", 

249 "_keymap", 

250 "_processors", 

251 "_tuplefilter", 

252 "_translated_indexes", 

253 "_unique_filters", 

254 "_key_to_index", 

255 ) 

256 

257 _keys: Sequence[str] 

258 

259 def __init__( 

260 self, 

261 keys: Sequence[str], 

262 extra: Optional[Sequence[Any]] = None, 

263 _processors: Optional[_ProcessorsType] = None, 

264 _tuplefilter: Optional[_TupleGetterType] = None, 

265 _translated_indexes: Optional[Sequence[int]] = None, 

266 _unique_filters: Optional[Sequence[Callable[[Any], Any]]] = None, 

267 ): 

268 self._keys = list(keys) 

269 self._tuplefilter = _tuplefilter 

270 self._translated_indexes = _translated_indexes 

271 self._unique_filters = _unique_filters 

272 if extra: 

273 assert len(self._keys) == len(extra) 

274 recs_names = [ 

275 ( 

276 (name,) + (extras if extras else ()), 

277 (index, name, extras), 

278 ) 

279 for index, (name, extras) in enumerate(zip(self._keys, extra)) 

280 ] 

281 else: 

282 recs_names = [ 

283 ((name,), (index, name, ())) 

284 for index, name in enumerate(self._keys) 

285 ] 

286 

287 self._keymap = {key: rec for keys, rec in recs_names for key in keys} 

288 

289 self._processors = _processors 

290 

291 self._key_to_index = self._make_key_to_index(self._keymap, 0) 

292 

293 def _has_key(self, key: object) -> bool: 

294 return key in self._keymap 

295 

296 def _for_freeze(self) -> ResultMetaData: 

297 unique_filters = self._unique_filters 

298 if unique_filters and self._tuplefilter: 

299 unique_filters = self._tuplefilter(unique_filters) 

300 

301 # TODO: are we freezing the result with or without uniqueness 

302 # applied? 

303 return SimpleResultMetaData( 

304 self._keys, 

305 extra=[self._keymap[key][2] for key in self._keys], 

306 _unique_filters=unique_filters, 

307 ) 

308 

309 def __getstate__(self) -> Dict[str, Any]: 

310 return { 

311 "_keys": self._keys, 

312 "_translated_indexes": self._translated_indexes, 

313 } 

314 

315 def __setstate__(self, state: Dict[str, Any]) -> None: 

316 if state["_translated_indexes"]: 

317 _translated_indexes = state["_translated_indexes"] 

318 _tuplefilter = tuplegetter(*_translated_indexes) 

319 else: 

320 _translated_indexes = _tuplefilter = None 

321 self.__init__( # type: ignore 

322 state["_keys"], 

323 _translated_indexes=_translated_indexes, 

324 _tuplefilter=_tuplefilter, 

325 ) 

326 

327 def _index_for_key(self, key: Any, raiseerr: bool = True) -> int: 

328 if int in key.__class__.__mro__: 

329 key = self._keys[key] 

330 try: 

331 rec = self._keymap[key] 

332 except KeyError as ke: 

333 rec = self._key_fallback(key, ke, raiseerr) 

334 

335 return rec[0] # type: ignore[no-any-return] 

336 

337 def _indexes_for_keys(self, keys: Sequence[Any]) -> Sequence[int]: 

338 return [self._keymap[key][0] for key in keys] 

339 

340 def _metadata_for_keys( 

341 self, keys: Sequence[Any] 

342 ) -> Iterator[_KeyMapRecType]: 

343 for key in keys: 

344 if int in key.__class__.__mro__: 

345 key = self._keys[key] 

346 

347 try: 

348 rec = self._keymap[key] 

349 except KeyError as ke: 

350 rec = self._key_fallback(key, ke, True) 

351 

352 yield rec 

353 

354 def _reduce(self, keys: Sequence[Any]) -> ResultMetaData: 

355 try: 

356 metadata_for_keys = [ 

357 self._keymap[ 

358 self._keys[key] if int in key.__class__.__mro__ else key 

359 ] 

360 for key in keys 

361 ] 

362 except KeyError as ke: 

363 self._key_fallback(ke.args[0], ke, True) 

364 

365 indexes: Sequence[int] 

366 new_keys: Sequence[str] 

367 extra: Sequence[Any] 

368 indexes, new_keys, extra = zip(*metadata_for_keys) 

369 

370 if self._translated_indexes: 

371 indexes = [self._translated_indexes[idx] for idx in indexes] 

372 

373 tup = tuplegetter(*indexes) 

374 

375 new_metadata = SimpleResultMetaData( 

376 new_keys, 

377 extra=extra, 

378 _tuplefilter=tup, 

379 _translated_indexes=indexes, 

380 _processors=self._processors, 

381 _unique_filters=self._unique_filters, 

382 ) 

383 

384 return new_metadata 

385 

386 

387def result_tuple( 

388 fields: Sequence[str], extra: Optional[Any] = None 

389) -> Callable[[Iterable[Any]], Row[Unpack[TupleAny]]]: 

390 parent = SimpleResultMetaData(fields, extra) 

391 return functools.partial( 

392 Row, parent, parent._effective_processors, parent._key_to_index 

393 ) 

394 

395 

396# a symbol that indicates to internal Result methods that 

397# "no row is returned". We can't use None for those cases where a scalar 

398# filter is applied to rows. 

399class _NoRow(Enum): 

400 _NO_ROW = 0 

401 

402 

403_NO_ROW = _NoRow._NO_ROW 

404 

405 

406class ResultInternal(InPlaceGenerative, Generic[_R]): 

407 __slots__ = () 

408 

409 _real_result: Optional[Result[Unpack[TupleAny]]] = None 

410 _generate_rows: bool = True 

411 _row_logging_fn: Optional[Callable[[Any], Any]] 

412 

413 _unique_filter_state: Optional[_UniqueFilterStateType] = None 

414 _post_creational_filter: Optional[Callable[[Any], Any]] = None 

415 _is_cursor = False 

416 

417 _metadata: ResultMetaData 

418 

419 _source_supports_scalars: bool 

420 

421 def _fetchiter_impl( 

422 self, 

423 ) -> Iterator[_InterimRowType[Row[Unpack[TupleAny]]]]: 

424 raise NotImplementedError() 

425 

426 def _fetchone_impl( 

427 self, hard_close: bool = False 

428 ) -> Optional[_InterimRowType[Row[Unpack[TupleAny]]]]: 

429 raise NotImplementedError() 

430 

431 def _fetchmany_impl( 

432 self, size: Optional[int] = None 

433 ) -> List[_InterimRowType[Row[Unpack[TupleAny]]]]: 

434 raise NotImplementedError() 

435 

436 def _fetchall_impl( 

437 self, 

438 ) -> List[_InterimRowType[Row[Unpack[TupleAny]]]]: 

439 raise NotImplementedError() 

440 

441 def _soft_close(self, hard: bool = False) -> None: 

442 raise NotImplementedError() 

443 

444 @HasMemoized_ro_memoized_attribute 

445 def _row_getter(self) -> Optional[Callable[..., _R]]: 

446 real_result: Result[Unpack[TupleAny]] = ( 

447 self._real_result 

448 if self._real_result 

449 else cast("Result[Unpack[TupleAny]]", self) 

450 ) 

451 

452 if real_result._source_supports_scalars: 

453 if not self._generate_rows: 

454 return None 

455 else: 

456 _proc = Row 

457 

458 def process_row( 

459 metadata: ResultMetaData, 

460 processors: Optional[_ProcessorsType], 

461 key_to_index: Dict[_KeyType, int], 

462 scalar_obj: Any, 

463 ) -> Row[Unpack[TupleAny]]: 

464 return _proc( 

465 metadata, processors, key_to_index, (scalar_obj,) 

466 ) 

467 

468 else: 

469 process_row = Row # type: ignore 

470 

471 metadata = self._metadata 

472 

473 key_to_index = metadata._key_to_index 

474 processors = metadata._effective_processors 

475 tf = metadata._tuplefilter 

476 

477 if tf and not real_result._source_supports_scalars: 

478 if processors: 

479 processors = tf(processors) 

480 

481 _make_row_orig: Callable[..., _R] = functools.partial( # type: ignore # noqa E501 

482 process_row, metadata, processors, key_to_index 

483 ) 

484 

485 fixed_tf = tf 

486 

487 def make_row(row: _InterimRowType[Row[Unpack[TupleAny]]]) -> _R: 

488 return _make_row_orig(fixed_tf(row)) 

489 

490 else: 

491 make_row = functools.partial( # type: ignore 

492 process_row, metadata, processors, key_to_index 

493 ) 

494 

495 if real_result._row_logging_fn: 

496 _log_row = real_result._row_logging_fn 

497 _make_row = make_row 

498 

499 def make_row(row: _InterimRowType[Row[Unpack[TupleAny]]]) -> _R: 

500 return _log_row(_make_row(row)) # type: ignore 

501 

502 return make_row 

503 

504 @HasMemoized_ro_memoized_attribute 

505 def _iterator_getter(self) -> Callable[..., Iterator[_R]]: 

506 make_row = self._row_getter 

507 

508 post_creational_filter = self._post_creational_filter 

509 

510 if self._unique_filter_state: 

511 uniques, strategy = self._unique_strategy 

512 

513 def iterrows(self: Result[Unpack[TupleAny]]) -> Iterator[_R]: 

514 for raw_row in self._fetchiter_impl(): 

515 obj: _InterimRowType[Any] = ( 

516 make_row(raw_row) if make_row else raw_row 

517 ) 

518 hashed = strategy(obj) if strategy else obj 

519 if hashed in uniques: 

520 continue 

521 uniques.add(hashed) 

522 if post_creational_filter: 

523 obj = post_creational_filter(obj) 

524 yield obj # type: ignore 

525 

526 else: 

527 

528 def iterrows(self: Result[Unpack[TupleAny]]) -> Iterator[_R]: 

529 for raw_row in self._fetchiter_impl(): 

530 row: _InterimRowType[Any] = ( 

531 make_row(raw_row) if make_row else raw_row 

532 ) 

533 if post_creational_filter: 

534 row = post_creational_filter(row) 

535 yield row # type: ignore 

536 

537 return iterrows 

538 

539 def _raw_all_rows(self) -> List[_R]: 

540 make_row = self._row_getter 

541 assert make_row is not None 

542 rows = self._fetchall_impl() 

543 return [make_row(row) for row in rows] 

544 

545 def _allrows(self) -> List[_R]: 

546 post_creational_filter = self._post_creational_filter 

547 

548 make_row = self._row_getter 

549 

550 rows = self._fetchall_impl() 

551 made_rows: List[_InterimRowType[_R]] 

552 if make_row: 

553 made_rows = [make_row(row) for row in rows] 

554 else: 

555 made_rows = rows # type: ignore 

556 

557 interim_rows: List[_R] 

558 

559 if self._unique_filter_state: 

560 uniques, strategy = self._unique_strategy 

561 

562 interim_rows = [ 

563 made_row # type: ignore 

564 for made_row, sig_row in [ 

565 ( 

566 made_row, 

567 strategy(made_row) if strategy else made_row, 

568 ) 

569 for made_row in made_rows 

570 ] 

571 if sig_row not in uniques and not uniques.add(sig_row) # type: ignore # noqa: E501 

572 ] 

573 else: 

574 interim_rows = made_rows # type: ignore 

575 

576 if post_creational_filter: 

577 interim_rows = [ 

578 post_creational_filter(row) for row in interim_rows 

579 ] 

580 return interim_rows 

581 

582 @HasMemoized_ro_memoized_attribute 

583 def _onerow_getter( 

584 self, 

585 ) -> Callable[..., Union[Literal[_NoRow._NO_ROW], _R]]: 

586 make_row = self._row_getter 

587 

588 post_creational_filter = self._post_creational_filter 

589 

590 if self._unique_filter_state: 

591 uniques, strategy = self._unique_strategy 

592 

593 def onerow(self: Result[Unpack[TupleAny]]) -> Union[_NoRow, _R]: 

594 _onerow = self._fetchone_impl 

595 while True: 

596 row = _onerow() 

597 if row is None: 

598 return _NO_ROW 

599 else: 

600 obj: _InterimRowType[Any] = ( 

601 make_row(row) if make_row else row 

602 ) 

603 hashed = strategy(obj) if strategy else obj 

604 if hashed in uniques: 

605 continue 

606 else: 

607 uniques.add(hashed) 

608 if post_creational_filter: 

609 obj = post_creational_filter(obj) 

610 return obj # type: ignore 

611 

612 else: 

613 

614 def onerow(self: Result[Unpack[TupleAny]]) -> Union[_NoRow, _R]: 

615 row = self._fetchone_impl() 

616 if row is None: 

617 return _NO_ROW 

618 else: 

619 interim_row: _InterimRowType[Any] = ( 

620 make_row(row) if make_row else row 

621 ) 

622 if post_creational_filter: 

623 interim_row = post_creational_filter(interim_row) 

624 return interim_row # type: ignore 

625 

626 return onerow 

627 

628 @HasMemoized_ro_memoized_attribute 

629 def _manyrow_getter(self) -> Callable[..., List[_R]]: 

630 make_row = self._row_getter 

631 

632 post_creational_filter = self._post_creational_filter 

633 

634 if self._unique_filter_state: 

635 uniques, strategy = self._unique_strategy 

636 

637 def filterrows( 

638 make_row: Optional[Callable[..., _R]], 

639 rows: List[Any], 

640 strategy: Optional[Callable[[List[Any]], Any]], 

641 uniques: Set[Any], 

642 ) -> List[_R]: 

643 if make_row: 

644 rows = [make_row(row) for row in rows] 

645 

646 if strategy: 

647 made_rows = ( 

648 (made_row, strategy(made_row)) for made_row in rows 

649 ) 

650 else: 

651 made_rows = ((made_row, made_row) for made_row in rows) 

652 return [ 

653 made_row 

654 for made_row, sig_row in made_rows 

655 if sig_row not in uniques and not uniques.add(sig_row) # type: ignore # noqa: E501 

656 ] 

657 

658 def manyrows( 

659 self: ResultInternal[_R], num: Optional[int] 

660 ) -> List[_R]: 

661 collect: List[_R] = [] 

662 

663 _manyrows = self._fetchmany_impl 

664 

665 if num is None: 

666 # if None is passed, we don't know the default 

667 # manyrows number, DBAPI has this as cursor.arraysize 

668 # different DBAPIs / fetch strategies may be different. 

669 # do a fetch to find what the number is. if there are 

670 # only fewer rows left, then it doesn't matter. 

671 real_result = ( 

672 self._real_result 

673 if self._real_result 

674 else cast("Result[Unpack[TupleAny]]", self) 

675 ) 

676 if real_result._yield_per: 

677 num_required = num = real_result._yield_per 

678 else: 

679 rows = _manyrows(num) 

680 num = len(rows) 

681 assert make_row is not None 

682 collect.extend( 

683 filterrows(make_row, rows, strategy, uniques) 

684 ) 

685 num_required = num - len(collect) 

686 else: 

687 num_required = num 

688 

689 assert num is not None 

690 

691 while num_required: 

692 rows = _manyrows(num_required) 

693 if not rows: 

694 break 

695 

696 collect.extend( 

697 filterrows(make_row, rows, strategy, uniques) 

698 ) 

699 num_required = num - len(collect) 

700 

701 if post_creational_filter: 

702 collect = [post_creational_filter(row) for row in collect] 

703 return collect 

704 

705 else: 

706 

707 def manyrows( 

708 self: ResultInternal[_R], num: Optional[int] 

709 ) -> List[_R]: 

710 if num is None: 

711 real_result = ( 

712 self._real_result 

713 if self._real_result 

714 else cast("Result[Unpack[TupleAny]]", self) 

715 ) 

716 num = real_result._yield_per 

717 

718 rows: List[_InterimRowType[Any]] = self._fetchmany_impl(num) 

719 if make_row: 

720 rows = [make_row(row) for row in rows] 

721 if post_creational_filter: 

722 rows = [post_creational_filter(row) for row in rows] 

723 return rows # type: ignore 

724 

725 return manyrows 

726 

727 @overload 

728 def _only_one_row( 

729 self: ResultInternal[Row[_T, Unpack[TupleAny]]], 

730 raise_for_second_row: bool, 

731 raise_for_none: bool, 

732 scalar: Literal[True], 

733 ) -> _T: ... 

734 

735 @overload 

736 def _only_one_row( 

737 self, 

738 raise_for_second_row: bool, 

739 raise_for_none: Literal[True], 

740 scalar: bool, 

741 ) -> _R: ... 

742 

743 @overload 

744 def _only_one_row( 

745 self, 

746 raise_for_second_row: bool, 

747 raise_for_none: bool, 

748 scalar: bool, 

749 ) -> Optional[_R]: ... 

750 

751 def _only_one_row( 

752 self, 

753 raise_for_second_row: bool, 

754 raise_for_none: bool, 

755 scalar: bool, 

756 ) -> Optional[_R]: 

757 onerow = self._fetchone_impl 

758 

759 row: Optional[_InterimRowType[Any]] = onerow(hard_close=True) 

760 if row is None: 

761 if raise_for_none: 

762 raise exc.NoResultFound( 

763 "No row was found when one was required" 

764 ) 

765 else: 

766 return None 

767 

768 if scalar and self._source_supports_scalars: 

769 self._generate_rows = False 

770 make_row = None 

771 else: 

772 make_row = self._row_getter 

773 

774 try: 

775 row = make_row(row) if make_row else row 

776 except: 

777 self._soft_close(hard=True) 

778 raise 

779 

780 if raise_for_second_row: 

781 if self._unique_filter_state: 

782 # for no second row but uniqueness, need to essentially 

783 # consume the entire result :( 

784 uniques, strategy = self._unique_strategy 

785 

786 existing_row_hash = strategy(row) if strategy else row 

787 

788 while True: 

789 next_row: Any = onerow(hard_close=True) 

790 if next_row is None: 

791 next_row = _NO_ROW 

792 break 

793 

794 try: 

795 next_row = make_row(next_row) if make_row else next_row 

796 

797 if strategy: 

798 assert next_row is not _NO_ROW 

799 if existing_row_hash == strategy(next_row): 

800 continue 

801 elif row == next_row: 

802 continue 

803 # here, we have a row and it's different 

804 break 

805 except: 

806 self._soft_close(hard=True) 

807 raise 

808 else: 

809 next_row = onerow(hard_close=True) 

810 if next_row is None: 

811 next_row = _NO_ROW 

812 

813 if next_row is not _NO_ROW: 

814 self._soft_close(hard=True) 

815 raise exc.MultipleResultsFound( 

816 "Multiple rows were found when exactly one was required" 

817 if raise_for_none 

818 else "Multiple rows were found when one or none " 

819 "was required" 

820 ) 

821 else: 

822 # if we checked for second row then that would have 

823 # closed us :) 

824 self._soft_close(hard=True) 

825 

826 if not scalar: 

827 post_creational_filter = self._post_creational_filter 

828 if post_creational_filter: 

829 row = post_creational_filter(row) 

830 

831 if scalar and make_row: 

832 return row[0] # type: ignore 

833 else: 

834 return row # type: ignore 

835 

836 def _iter_impl(self) -> Iterator[_R]: 

837 return self._iterator_getter(self) 

838 

839 def _next_impl(self) -> _R: 

840 row = self._onerow_getter(self) 

841 if row is _NO_ROW: 

842 raise StopIteration() 

843 else: 

844 return row 

845 

846 @_generative 

847 def _column_slices(self, indexes: Sequence[_KeyIndexType]) -> Self: 

848 real_result = ( 

849 self._real_result 

850 if self._real_result 

851 else cast("Result[Any]", self) 

852 ) 

853 

854 if not real_result._source_supports_scalars or len(indexes) != 1: 

855 self._metadata = self._metadata._reduce(indexes) 

856 

857 assert self._generate_rows 

858 

859 return self 

860 

861 @HasMemoized.memoized_attribute 

862 def _unique_strategy(self) -> _UniqueFilterStateType: 

863 assert self._unique_filter_state is not None 

864 uniques, strategy = self._unique_filter_state 

865 

866 real_result = ( 

867 self._real_result 

868 if self._real_result is not None 

869 else cast("Result[Unpack[TupleAny]]", self) 

870 ) 

871 

872 if not strategy and self._metadata._unique_filters: 

873 if ( 

874 real_result._source_supports_scalars 

875 and not self._generate_rows 

876 ): 

877 strategy = self._metadata._unique_filters[0] 

878 else: 

879 filters = self._metadata._unique_filters 

880 if self._metadata._tuplefilter: 

881 filters = self._metadata._tuplefilter(filters) 

882 

883 strategy = operator.methodcaller("_filter_on_values", filters) 

884 return uniques, strategy 

885 

886 

887class _WithKeys: 

888 __slots__ = () 

889 

890 _metadata: ResultMetaData 

891 

892 # used mainly to share documentation on the keys method. 

893 def keys(self) -> RMKeyView: 

894 """Return an iterable view which yields the string keys that would 

895 be represented by each :class:`_engine.Row`. 

896 

897 The keys can represent the labels of the columns returned by a core 

898 statement or the names of the orm classes returned by an orm 

899 execution. 

900 

901 The view also can be tested for key containment using the Python 

902 ``in`` operator, which will test both for the string keys represented 

903 in the view, as well as for alternate keys such as column objects. 

904 

905 .. versionchanged:: 1.4 a key view object is returned rather than a 

906 plain list. 

907 

908 

909 """ 

910 return self._metadata.keys 

911 

912 

913class Result(_WithKeys, ResultInternal[Row[Unpack[_Ts]]]): 

914 """Represent a set of database results. 

915 

916 .. versionadded:: 1.4 The :class:`_engine.Result` object provides a 

917 completely updated usage model and calling facade for SQLAlchemy 

918 Core and SQLAlchemy ORM. In Core, it forms the basis of the 

919 :class:`_engine.CursorResult` object which replaces the previous 

920 :class:`_engine.ResultProxy` interface. When using the ORM, a 

921 higher level object called :class:`_engine.ChunkedIteratorResult` 

922 is normally used. 

923 

924 .. note:: In SQLAlchemy 1.4 and above, this object is 

925 used for ORM results returned by :meth:`_orm.Session.execute`, which can 

926 yield instances of ORM mapped objects either individually or within 

927 tuple-like rows. Note that the :class:`_engine.Result` object does not 

928 deduplicate instances or rows automatically as is the case with the 

929 legacy :class:`_orm.Query` object. For in-Python de-duplication of 

930 instances or rows, use the :meth:`_engine.Result.unique` modifier 

931 method. 

932 

933 .. seealso:: 

934 

935 :ref:`tutorial_fetching_rows` - in the :doc:`/tutorial/index` 

936 

937 """ 

938 

939 __slots__ = ("_metadata", "__dict__") 

940 

941 _row_logging_fn: Optional[ 

942 Callable[[Row[Unpack[TupleAny]]], Row[Unpack[TupleAny]]] 

943 ] = None 

944 

945 _source_supports_scalars: bool = False 

946 

947 _yield_per: Optional[int] = None 

948 

949 _attributes: util.immutabledict[Any, Any] = util.immutabledict() 

950 

951 def __init__(self, cursor_metadata: ResultMetaData): 

952 self._metadata = cursor_metadata 

953 

954 def __enter__(self) -> Self: 

955 return self 

956 

957 def __exit__(self, type_: Any, value: Any, traceback: Any) -> None: 

958 self.close() 

959 

960 def close(self) -> None: 

961 """close this :class:`_engine.Result`. 

962 

963 The behavior of this method is implementation specific, and is 

964 not implemented by default. The method should generally end 

965 the resources in use by the result object and also cause any 

966 subsequent iteration or row fetching to raise 

967 :class:`.ResourceClosedError`. 

968 

969 .. versionadded:: 1.4.27 - ``.close()`` was previously not generally 

970 available for all :class:`_engine.Result` classes, instead only 

971 being available on the :class:`_engine.CursorResult` returned for 

972 Core statement executions. As most other result objects, namely the 

973 ones used by the ORM, are proxying a :class:`_engine.CursorResult` 

974 in any case, this allows the underlying cursor result to be closed 

975 from the outside facade for the case when the ORM query is using 

976 the ``yield_per`` execution option where it does not immediately 

977 exhaust and autoclose the database cursor. 

978 

979 """ 

980 self._soft_close(hard=True) 

981 

982 @property 

983 def _soft_closed(self) -> bool: 

984 raise NotImplementedError() 

985 

986 @property 

987 def closed(self) -> bool: 

988 """return ``True`` if this :class:`_engine.Result` reports .closed 

989 

990 .. versionadded:: 1.4.43 

991 

992 """ 

993 raise NotImplementedError() 

994 

995 @_generative 

996 def yield_per(self, num: int) -> Self: 

997 """Configure the row-fetching strategy to fetch ``num`` rows at a time. 

998 

999 This impacts the underlying behavior of the result when iterating over 

1000 the result object, or otherwise making use of methods such as 

1001 :meth:`_engine.Result.fetchone` that return one row at a time. Data 

1002 from the underlying cursor or other data source will be buffered up to 

1003 this many rows in memory, and the buffered collection will then be 

1004 yielded out one row at a time or as many rows are requested. Each time 

1005 the buffer clears, it will be refreshed to this many rows or as many 

1006 rows remain if fewer remain. 

1007 

1008 The :meth:`_engine.Result.yield_per` method is generally used in 

1009 conjunction with the 

1010 :paramref:`_engine.Connection.execution_options.stream_results` 

1011 execution option, which will allow the database dialect in use to make 

1012 use of a server side cursor, if the DBAPI supports a specific "server 

1013 side cursor" mode separate from its default mode of operation. 

1014 

1015 .. tip:: 

1016 

1017 Consider using the 

1018 :paramref:`_engine.Connection.execution_options.yield_per` 

1019 execution option, which will simultaneously set 

1020 :paramref:`_engine.Connection.execution_options.stream_results` 

1021 to ensure the use of server side cursors, as well as automatically 

1022 invoke the :meth:`_engine.Result.yield_per` method to establish 

1023 a fixed row buffer size at once. 

1024 

1025 The :paramref:`_engine.Connection.execution_options.yield_per` 

1026 execution option is available for ORM operations, with 

1027 :class:`_orm.Session`-oriented use described at 

1028 :ref:`orm_queryguide_yield_per`. The Core-only version which works 

1029 with :class:`_engine.Connection` is new as of SQLAlchemy 1.4.40. 

1030 

1031 .. versionadded:: 1.4 

1032 

1033 :param num: number of rows to fetch each time the buffer is refilled. 

1034 If set to a value below 1, fetches all rows for the next buffer. 

1035 

1036 .. seealso:: 

1037 

1038 :ref:`engine_stream_results` - describes Core behavior for 

1039 :meth:`_engine.Result.yield_per` 

1040 

1041 :ref:`orm_queryguide_yield_per` - in the :ref:`queryguide_toplevel` 

1042 

1043 """ 

1044 self._yield_per = num 

1045 return self 

1046 

1047 @_generative 

1048 def unique(self, strategy: Optional[_UniqueFilterType] = None) -> Self: 

1049 """Apply unique filtering to the objects returned by this 

1050 :class:`_engine.Result`. 

1051 

1052 When this filter is applied with no arguments, the rows or objects 

1053 returned will filtered such that each row is returned uniquely. The 

1054 algorithm used to determine this uniqueness is by default the Python 

1055 hashing identity of the whole tuple. In some cases a specialized 

1056 per-entity hashing scheme may be used, such as when using the ORM, a 

1057 scheme is applied which works against the primary key identity of 

1058 returned objects. 

1059 

1060 The unique filter is applied **after all other filters**, which means 

1061 if the columns returned have been refined using a method such as the 

1062 :meth:`_engine.Result.columns` or :meth:`_engine.Result.scalars` 

1063 method, the uniquing is applied to **only the column or columns 

1064 returned**. This occurs regardless of the order in which these 

1065 methods have been called upon the :class:`_engine.Result` object. 

1066 

1067 The unique filter also changes the calculus used for methods like 

1068 :meth:`_engine.Result.fetchmany` and :meth:`_engine.Result.partitions`. 

1069 When using :meth:`_engine.Result.unique`, these methods will continue 

1070 to yield the number of rows or objects requested, after uniquing 

1071 has been applied. However, this necessarily impacts the buffering 

1072 behavior of the underlying cursor or datasource, such that multiple 

1073 underlying calls to ``cursor.fetchmany()`` may be necessary in order 

1074 to accumulate enough objects in order to provide a unique collection 

1075 of the requested size. 

1076 

1077 :param strategy: a callable that will be applied to rows or objects 

1078 being iterated, which should return an object that represents the 

1079 unique value of the row. A Python ``set()`` is used to store 

1080 these identities. If not passed, a default uniqueness strategy 

1081 is used which may have been assembled by the source of this 

1082 :class:`_engine.Result` object. 

1083 

1084 """ 

1085 self._unique_filter_state = (set(), strategy) 

1086 return self 

1087 

1088 def columns(self, *col_expressions: _KeyIndexType) -> Self: 

1089 r"""Establish the columns that should be returned in each row. 

1090 

1091 This method may be used to limit the columns returned as well 

1092 as to reorder them. The given list of expressions are normally 

1093 a series of integers or string key names. They may also be 

1094 appropriate :class:`.ColumnElement` objects which correspond to 

1095 a given statement construct. 

1096 

1097 .. versionchanged:: 2.0 Due to a bug in 1.4, the 

1098 :meth:`_engine.Result.columns` method had an incorrect behavior 

1099 where calling upon the method with just one index would cause the 

1100 :class:`_engine.Result` object to yield scalar values rather than 

1101 :class:`_engine.Row` objects. In version 2.0, this behavior 

1102 has been corrected such that calling upon 

1103 :meth:`_engine.Result.columns` with a single index will 

1104 produce a :class:`_engine.Result` object that continues 

1105 to yield :class:`_engine.Row` objects, which include 

1106 only a single column. 

1107 

1108 E.g.:: 

1109 

1110 statement = select(table.c.x, table.c.y, table.c.z) 

1111 result = connection.execute(statement) 

1112 

1113 for z, y in result.columns("z", "y"): 

1114 ... 

1115 

1116 Example of using the column objects from the statement itself:: 

1117 

1118 for z, y in result.columns( 

1119 statement.selected_columns.c.z, statement.selected_columns.c.y 

1120 ): 

1121 ... 

1122 

1123 .. versionadded:: 1.4 

1124 

1125 :param \*col_expressions: indicates columns to be returned. Elements 

1126 may be integer row indexes, string column names, or appropriate 

1127 :class:`.ColumnElement` objects corresponding to a select construct. 

1128 

1129 :return: this :class:`_engine.Result` object with the modifications 

1130 given. 

1131 

1132 """ 

1133 return self._column_slices(col_expressions) 

1134 

1135 @overload 

1136 def scalars(self: Result[_T, Unpack[TupleAny]]) -> ScalarResult[_T]: ... 

1137 

1138 @overload 

1139 def scalars( 

1140 self: Result[_T, Unpack[TupleAny]], index: Literal[0] 

1141 ) -> ScalarResult[_T]: ... 

1142 

1143 @overload 

1144 def scalars(self, index: _KeyIndexType = 0) -> ScalarResult[Any]: ... 

1145 

1146 def scalars(self, index: _KeyIndexType = 0) -> ScalarResult[Any]: 

1147 """Return a :class:`_engine.ScalarResult` filtering object which 

1148 will return single elements rather than :class:`_row.Row` objects. 

1149 

1150 E.g.:: 

1151 

1152 >>> result = conn.execute(text("select int_id from table")) 

1153 >>> result.scalars().all() 

1154 [1, 2, 3] 

1155 

1156 When results are fetched from the :class:`_engine.ScalarResult` 

1157 filtering object, the single column-row that would be returned by the 

1158 :class:`_engine.Result` is instead returned as the column's value. 

1159 

1160 .. versionadded:: 1.4 

1161 

1162 :param index: integer or row key indicating the column to be fetched 

1163 from each row, defaults to ``0`` indicating the first column. 

1164 

1165 :return: a new :class:`_engine.ScalarResult` filtering object referring 

1166 to this :class:`_engine.Result` object. 

1167 

1168 """ 

1169 return ScalarResult(self, index) 

1170 

1171 def _getter( 

1172 self, key: _KeyIndexType, raiseerr: bool = True 

1173 ) -> Optional[Callable[[Row[Unpack[TupleAny]]], Any]]: 

1174 """return a callable that will retrieve the given key from a 

1175 :class:`_engine.Row`. 

1176 

1177 """ 

1178 if self._source_supports_scalars: 

1179 raise NotImplementedError( 

1180 "can't use this function in 'only scalars' mode" 

1181 ) 

1182 return self._metadata._getter(key, raiseerr) 

1183 

1184 def _tuple_getter(self, keys: Sequence[_KeyIndexType]) -> _TupleGetterType: 

1185 """return a callable that will retrieve the given keys from a 

1186 :class:`_engine.Row`. 

1187 

1188 """ 

1189 if self._source_supports_scalars: 

1190 raise NotImplementedError( 

1191 "can't use this function in 'only scalars' mode" 

1192 ) 

1193 return self._metadata._row_as_tuple_getter(keys) 

1194 

1195 def mappings(self) -> MappingResult: 

1196 """Apply a mappings filter to returned rows, returning an instance of 

1197 :class:`_engine.MappingResult`. 

1198 

1199 When this filter is applied, fetching rows will return 

1200 :class:`_engine.RowMapping` objects instead of :class:`_engine.Row` 

1201 objects. 

1202 

1203 .. versionadded:: 1.4 

1204 

1205 :return: a new :class:`_engine.MappingResult` filtering object 

1206 referring to this :class:`_engine.Result` object. 

1207 

1208 """ 

1209 

1210 return MappingResult(self) 

1211 

1212 @property 

1213 @deprecated( 

1214 "2.1.0", 

1215 "The :attr:`.Result.t` method is deprecated, :class:`.Row` " 

1216 "now behaves like a tuple and can unpack types directly.", 

1217 ) 

1218 def t(self) -> TupleResult[Tuple[Unpack[_Ts]]]: 

1219 """Apply a "typed tuple" typing filter to returned rows. 

1220 

1221 The :attr:`_engine.Result.t` attribute is a synonym for 

1222 calling the :meth:`_engine.Result.tuples` method. 

1223 

1224 .. versionadded:: 2.0 

1225 

1226 .. seealso:: 

1227 

1228 :ref:`change_10635` - describes a migration path from this 

1229 workaround for SQLAlchemy 2.1. 

1230 

1231 """ 

1232 return self # type: ignore 

1233 

1234 @deprecated( 

1235 "2.1.0", 

1236 "The :meth:`.Result.tuples` method is deprecated, :class:`.Row` " 

1237 "now behaves like a tuple and can unpack types directly.", 

1238 ) 

1239 def tuples(self) -> TupleResult[Tuple[Unpack[_Ts]]]: 

1240 """Apply a "typed tuple" typing filter to returned rows. 

1241 

1242 This method returns the same :class:`_engine.Result` object 

1243 at runtime, 

1244 however annotates as returning a :class:`_engine.TupleResult` object 

1245 that will indicate to :pep:`484` typing tools that plain typed 

1246 ``Tuple`` instances are returned rather than rows. This allows 

1247 tuple unpacking and ``__getitem__`` access of :class:`_engine.Row` 

1248 objects to by typed, for those cases where the statement invoked 

1249 itself included typing information. 

1250 

1251 .. versionadded:: 2.0 

1252 

1253 :return: the :class:`_engine.TupleResult` type at typing time. 

1254 

1255 .. seealso:: 

1256 

1257 :ref:`change_10635` - describes a migration path from this 

1258 workaround for SQLAlchemy 2.1. 

1259 

1260 :attr:`_engine.Result.t` - shorter synonym 

1261 

1262 :attr:`_engine.Row._t` - :class:`_engine.Row` version 

1263 

1264 """ 

1265 

1266 return self # type: ignore 

1267 

1268 def _raw_row_iterator(self) -> Iterator[_RowData]: 

1269 """Return a safe iterator that yields raw row data. 

1270 

1271 This is used by the :meth:`_engine.Result.merge` method 

1272 to merge multiple compatible results together. 

1273 

1274 """ 

1275 raise NotImplementedError() 

1276 

1277 def __iter__(self) -> Iterator[Row[Unpack[_Ts]]]: 

1278 return self._iter_impl() 

1279 

1280 def __next__(self) -> Row[Unpack[_Ts]]: 

1281 return self._next_impl() 

1282 

1283 def partitions( 

1284 self, size: Optional[int] = None 

1285 ) -> Iterator[Sequence[Row[Unpack[_Ts]]]]: 

1286 """Iterate through sub-lists of rows of the size given. 

1287 

1288 Each list will be of the size given, excluding the last list to 

1289 be yielded, which may have a small number of rows. No empty 

1290 lists will be yielded. 

1291 

1292 The result object is automatically closed when the iterator 

1293 is fully consumed. 

1294 

1295 Note that the backend driver will usually buffer the entire result 

1296 ahead of time unless the 

1297 :paramref:`.Connection.execution_options.stream_results` execution 

1298 option is used indicating that the driver should not pre-buffer 

1299 results, if possible. Not all drivers support this option and 

1300 the option is silently ignored for those who do not. 

1301 

1302 When using the ORM, the :meth:`_engine.Result.partitions` method 

1303 is typically more effective from a memory perspective when it is 

1304 combined with use of the 

1305 :ref:`yield_per execution option <orm_queryguide_yield_per>`, 

1306 which instructs both the DBAPI driver to use server side cursors, 

1307 if available, as well as instructs the ORM loading internals to only 

1308 build a certain amount of ORM objects from a result at a time before 

1309 yielding them out. 

1310 

1311 .. versionadded:: 1.4 

1312 

1313 :param size: indicate the maximum number of rows to be present 

1314 in each list yielded. If None, makes use of the value set by 

1315 the :meth:`_engine.Result.yield_per`, method, if it were called, 

1316 or the :paramref:`_engine.Connection.execution_options.yield_per` 

1317 execution option, which is equivalent in this regard. If 

1318 yield_per weren't set, it makes use of the 

1319 :meth:`_engine.Result.fetchmany` default, which may be backend 

1320 specific and not well defined. 

1321 

1322 :return: iterator of lists 

1323 

1324 .. seealso:: 

1325 

1326 :ref:`engine_stream_results` 

1327 

1328 :ref:`orm_queryguide_yield_per` - in the :ref:`queryguide_toplevel` 

1329 

1330 """ 

1331 

1332 getter = self._manyrow_getter 

1333 

1334 while True: 

1335 partition = getter(self, size) 

1336 if partition: 

1337 yield partition 

1338 else: 

1339 break 

1340 

1341 def fetchall(self) -> Sequence[Row[Unpack[_Ts]]]: 

1342 """A synonym for the :meth:`_engine.Result.all` method.""" 

1343 

1344 return self._allrows() 

1345 

1346 def fetchone(self) -> Optional[Row[Unpack[_Ts]]]: 

1347 """Fetch one row. 

1348 

1349 When all rows are exhausted, returns None. 

1350 

1351 This method is provided for backwards compatibility with 

1352 SQLAlchemy 1.x.x. 

1353 

1354 To fetch the first row of a result only, use the 

1355 :meth:`_engine.Result.first` method. To iterate through all 

1356 rows, iterate the :class:`_engine.Result` object directly. 

1357 

1358 :return: a :class:`_engine.Row` object if no filters are applied, 

1359 or ``None`` if no rows remain. 

1360 

1361 """ 

1362 row = self._onerow_getter(self) 

1363 if row is _NO_ROW: 

1364 return None 

1365 else: 

1366 return row 

1367 

1368 def fetchmany( 

1369 self, size: Optional[int] = None 

1370 ) -> Sequence[Row[Unpack[_Ts]]]: 

1371 """Fetch many rows. 

1372 

1373 When all rows are exhausted, returns an empty sequence. 

1374 

1375 This method is provided for backwards compatibility with 

1376 SQLAlchemy 1.x.x. 

1377 

1378 To fetch rows in groups, use the :meth:`_engine.Result.partitions` 

1379 method. 

1380 

1381 :return: a sequence of :class:`_engine.Row` objects. 

1382 

1383 .. seealso:: 

1384 

1385 :meth:`_engine.Result.partitions` 

1386 

1387 """ 

1388 

1389 return self._manyrow_getter(self, size) 

1390 

1391 def all(self) -> Sequence[Row[Unpack[_Ts]]]: 

1392 """Return all rows in a sequence. 

1393 

1394 Closes the result set after invocation. Subsequent invocations 

1395 will return an empty sequence. 

1396 

1397 .. versionadded:: 1.4 

1398 

1399 :return: a sequence of :class:`_engine.Row` objects. 

1400 

1401 .. seealso:: 

1402 

1403 :ref:`engine_stream_results` - How to stream a large result set 

1404 without loading it completely in python. 

1405 

1406 """ 

1407 

1408 return self._allrows() 

1409 

1410 def first(self) -> Optional[Row[Unpack[_Ts]]]: 

1411 """Fetch the first row or ``None`` if no row is present. 

1412 

1413 Closes the result set and discards remaining rows. 

1414 

1415 .. note:: This method returns one **row**, e.g. tuple, by default. 

1416 To return exactly one single scalar value, that is, the first 

1417 column of the first row, use the 

1418 :meth:`_engine.Result.scalar` method, 

1419 or combine :meth:`_engine.Result.scalars` and 

1420 :meth:`_engine.Result.first`. 

1421 

1422 Additionally, in contrast to the behavior of the legacy ORM 

1423 :meth:`_orm.Query.first` method, **no limit is applied** to the 

1424 SQL query which was invoked to produce this 

1425 :class:`_engine.Result`; 

1426 for a DBAPI driver that buffers results in memory before yielding 

1427 rows, all rows will be sent to the Python process and all but 

1428 the first row will be discarded. 

1429 

1430 .. seealso:: 

1431 

1432 :ref:`migration_20_unify_select` 

1433 

1434 :return: a :class:`_engine.Row` object, or None 

1435 if no rows remain. 

1436 

1437 .. seealso:: 

1438 

1439 :meth:`_engine.Result.scalar` 

1440 

1441 :meth:`_engine.Result.one` 

1442 

1443 """ 

1444 

1445 return self._only_one_row( 

1446 raise_for_second_row=False, raise_for_none=False, scalar=False 

1447 ) 

1448 

1449 def one_or_none(self) -> Optional[Row[Unpack[_Ts]]]: 

1450 """Return at most one result or raise an exception. 

1451 

1452 Returns ``None`` if the result has no rows. 

1453 Raises :class:`.MultipleResultsFound` 

1454 if multiple rows are returned. 

1455 

1456 .. versionadded:: 1.4 

1457 

1458 :return: The first :class:`_engine.Row` or ``None`` if no row 

1459 is available. 

1460 

1461 :raises: :class:`.MultipleResultsFound` 

1462 

1463 .. seealso:: 

1464 

1465 :meth:`_engine.Result.first` 

1466 

1467 :meth:`_engine.Result.one` 

1468 

1469 """ 

1470 return self._only_one_row( 

1471 raise_for_second_row=True, raise_for_none=False, scalar=False 

1472 ) 

1473 

1474 def scalar_one(self: Result[_T, Unpack[TupleAny]]) -> _T: 

1475 """Return exactly one scalar result or raise an exception. 

1476 

1477 This is equivalent to calling :meth:`_engine.Result.scalars` and 

1478 then :meth:`_engine.ScalarResult.one`. 

1479 

1480 .. seealso:: 

1481 

1482 :meth:`_engine.ScalarResult.one` 

1483 

1484 :meth:`_engine.Result.scalars` 

1485 

1486 """ 

1487 return self._only_one_row( 

1488 raise_for_second_row=True, raise_for_none=True, scalar=True 

1489 ) 

1490 

1491 def scalar_one_or_none(self: Result[_T, Unpack[TupleAny]]) -> Optional[_T]: 

1492 """Return exactly one scalar result or ``None``. 

1493 

1494 This is equivalent to calling :meth:`_engine.Result.scalars` and 

1495 then :meth:`_engine.ScalarResult.one_or_none`. 

1496 

1497 .. seealso:: 

1498 

1499 :meth:`_engine.ScalarResult.one_or_none` 

1500 

1501 :meth:`_engine.Result.scalars` 

1502 

1503 """ 

1504 return self._only_one_row( 

1505 raise_for_second_row=True, raise_for_none=False, scalar=True 

1506 ) 

1507 

1508 def one(self) -> Row[Unpack[_Ts]]: 

1509 """Return exactly one row or raise an exception. 

1510 

1511 Raises :class:`_exc.NoResultFound` if the result returns no 

1512 rows, or :class:`_exc.MultipleResultsFound` if multiple rows 

1513 would be returned. 

1514 

1515 .. note:: This method returns one **row**, e.g. tuple, by default. 

1516 To return exactly one single scalar value, that is, the first 

1517 column of the first row, use the 

1518 :meth:`_engine.Result.scalar_one` method, or combine 

1519 :meth:`_engine.Result.scalars` and 

1520 :meth:`_engine.Result.one`. 

1521 

1522 .. versionadded:: 1.4 

1523 

1524 :return: The first :class:`_engine.Row`. 

1525 

1526 :raises: :class:`.MultipleResultsFound`, :class:`.NoResultFound` 

1527 

1528 .. seealso:: 

1529 

1530 :meth:`_engine.Result.first` 

1531 

1532 :meth:`_engine.Result.one_or_none` 

1533 

1534 :meth:`_engine.Result.scalar_one` 

1535 

1536 """ 

1537 return self._only_one_row( 

1538 raise_for_second_row=True, raise_for_none=True, scalar=False 

1539 ) 

1540 

1541 def scalar(self: Result[_T, Unpack[TupleAny]]) -> Optional[_T]: 

1542 """Fetch the first column of the first row, and close the result set. 

1543 

1544 Returns ``None`` if there are no rows to fetch. 

1545 

1546 No validation is performed to test if additional rows remain. 

1547 

1548 After calling this method, the object is fully closed, 

1549 e.g. the :meth:`_engine.CursorResult.close` 

1550 method will have been called. 

1551 

1552 :return: a Python scalar value, or ``None`` if no rows remain. 

1553 

1554 """ 

1555 return self._only_one_row( 

1556 raise_for_second_row=False, raise_for_none=False, scalar=True 

1557 ) 

1558 

1559 def freeze(self) -> FrozenResult[Unpack[_Ts]]: 

1560 """Return a callable object that will produce copies of this 

1561 :class:`_engine.Result` when invoked. 

1562 

1563 The callable object returned is an instance of 

1564 :class:`_engine.FrozenResult`. 

1565 

1566 This is used for result set caching. The method must be called 

1567 on the result when it has been unconsumed, and calling the method 

1568 will consume the result fully. When the :class:`_engine.FrozenResult` 

1569 is retrieved from a cache, it can be called any number of times where 

1570 it will produce a new :class:`_engine.Result` object each time 

1571 against its stored set of rows. 

1572 

1573 .. seealso:: 

1574 

1575 :ref:`do_orm_execute_re_executing` - example usage within the 

1576 ORM to implement a result-set cache. 

1577 

1578 """ 

1579 

1580 return FrozenResult(self) 

1581 

1582 def merge( 

1583 self, *others: Result[Unpack[TupleAny]] 

1584 ) -> MergedResult[Unpack[TupleAny]]: 

1585 """Merge this :class:`_engine.Result` with other compatible result 

1586 objects. 

1587 

1588 The object returned is an instance of :class:`_engine.MergedResult`, 

1589 which will be composed of iterators from the given result 

1590 objects. 

1591 

1592 The new result will use the metadata from this result object. 

1593 The subsequent result objects must be against an identical 

1594 set of result / cursor metadata, otherwise the behavior is 

1595 undefined. 

1596 

1597 """ 

1598 return MergedResult(self._metadata, (self,) + others) 

1599 

1600 

1601class FilterResult(ResultInternal[_R]): 

1602 """A wrapper for a :class:`_engine.Result` that returns objects other than 

1603 :class:`_engine.Row` objects, such as dictionaries or scalar objects. 

1604 

1605 :class:`_engine.FilterResult` is the common base for additional result 

1606 APIs including :class:`_engine.MappingResult`, 

1607 :class:`_engine.ScalarResult` and :class:`_engine.AsyncResult`. 

1608 

1609 """ 

1610 

1611 __slots__ = ( 

1612 "_real_result", 

1613 "_post_creational_filter", 

1614 "_metadata", 

1615 "_unique_filter_state", 

1616 "__dict__", 

1617 ) 

1618 

1619 _post_creational_filter: Optional[Callable[[Any], Any]] 

1620 

1621 _real_result: Result[Unpack[TupleAny]] 

1622 

1623 def __enter__(self) -> Self: 

1624 return self 

1625 

1626 def __exit__(self, type_: Any, value: Any, traceback: Any) -> None: 

1627 self._real_result.__exit__(type_, value, traceback) 

1628 

1629 @_generative 

1630 def yield_per(self, num: int) -> Self: 

1631 """Configure the row-fetching strategy to fetch ``num`` rows at a time. 

1632 

1633 The :meth:`_engine.FilterResult.yield_per` method is a pass through 

1634 to the :meth:`_engine.Result.yield_per` method. See that method's 

1635 documentation for usage notes. 

1636 

1637 .. versionadded:: 1.4.40 - added :meth:`_engine.FilterResult.yield_per` 

1638 so that the method is available on all result set implementations 

1639 

1640 .. seealso:: 

1641 

1642 :ref:`engine_stream_results` - describes Core behavior for 

1643 :meth:`_engine.Result.yield_per` 

1644 

1645 :ref:`orm_queryguide_yield_per` - in the :ref:`queryguide_toplevel` 

1646 

1647 """ 

1648 self._real_result = self._real_result.yield_per(num) 

1649 return self 

1650 

1651 def _soft_close(self, hard: bool = False) -> None: 

1652 self._real_result._soft_close(hard=hard) 

1653 

1654 @property 

1655 def _soft_closed(self) -> bool: 

1656 return self._real_result._soft_closed 

1657 

1658 @property 

1659 def closed(self) -> bool: 

1660 """Return ``True`` if the underlying :class:`_engine.Result` reports 

1661 closed 

1662 

1663 .. versionadded:: 1.4.43 

1664 

1665 """ 

1666 return self._real_result.closed 

1667 

1668 def close(self) -> None: 

1669 """Close this :class:`_engine.FilterResult`. 

1670 

1671 .. versionadded:: 1.4.43 

1672 

1673 """ 

1674 self._real_result.close() 

1675 

1676 @property 

1677 def _attributes(self) -> Dict[Any, Any]: 

1678 return self._real_result._attributes 

1679 

1680 def _fetchiter_impl( 

1681 self, 

1682 ) -> Iterator[_InterimRowType[Row[Unpack[TupleAny]]]]: 

1683 return self._real_result._fetchiter_impl() 

1684 

1685 def _fetchone_impl( 

1686 self, hard_close: bool = False 

1687 ) -> Optional[_InterimRowType[Row[Unpack[TupleAny]]]]: 

1688 return self._real_result._fetchone_impl(hard_close=hard_close) 

1689 

1690 def _fetchall_impl( 

1691 self, 

1692 ) -> List[_InterimRowType[Row[Unpack[TupleAny]]]]: 

1693 return self._real_result._fetchall_impl() 

1694 

1695 def _fetchmany_impl( 

1696 self, size: Optional[int] = None 

1697 ) -> List[_InterimRowType[Row[Unpack[TupleAny]]]]: 

1698 return self._real_result._fetchmany_impl(size=size) 

1699 

1700 

1701class ScalarResult(FilterResult[_R]): 

1702 """A wrapper for a :class:`_engine.Result` that returns scalar values 

1703 rather than :class:`_row.Row` values. 

1704 

1705 The :class:`_engine.ScalarResult` object is acquired by calling the 

1706 :meth:`_engine.Result.scalars` method. 

1707 

1708 A special limitation of :class:`_engine.ScalarResult` is that it has 

1709 no ``fetchone()`` method; since the semantics of ``fetchone()`` are that 

1710 the ``None`` value indicates no more results, this is not compatible 

1711 with :class:`_engine.ScalarResult` since there is no way to distinguish 

1712 between ``None`` as a row value versus ``None`` as an indicator. Use 

1713 ``next(result)`` to receive values individually. 

1714 

1715 """ 

1716 

1717 __slots__ = () 

1718 

1719 _generate_rows = False 

1720 

1721 _post_creational_filter: Optional[Callable[[Any], Any]] 

1722 

1723 def __init__( 

1724 self, real_result: Result[Unpack[TupleAny]], index: _KeyIndexType 

1725 ): 

1726 self._real_result = real_result 

1727 

1728 if real_result._source_supports_scalars: 

1729 self._metadata = real_result._metadata 

1730 self._post_creational_filter = None 

1731 else: 

1732 self._metadata = real_result._metadata._reduce([index]) 

1733 self._post_creational_filter = operator.itemgetter(0) 

1734 

1735 self._unique_filter_state = real_result._unique_filter_state 

1736 

1737 def unique(self, strategy: Optional[_UniqueFilterType] = None) -> Self: 

1738 """Apply unique filtering to the objects returned by this 

1739 :class:`_engine.ScalarResult`. 

1740 

1741 See :meth:`_engine.Result.unique` for usage details. 

1742 

1743 """ 

1744 self._unique_filter_state = (set(), strategy) 

1745 return self 

1746 

1747 def partitions(self, size: Optional[int] = None) -> Iterator[Sequence[_R]]: 

1748 """Iterate through sub-lists of elements of the size given. 

1749 

1750 Equivalent to :meth:`_engine.Result.partitions` except that 

1751 scalar values, rather than :class:`_engine.Row` objects, 

1752 are returned. 

1753 

1754 """ 

1755 

1756 getter = self._manyrow_getter 

1757 

1758 while True: 

1759 partition = getter(self, size) 

1760 if partition: 

1761 yield partition 

1762 else: 

1763 break 

1764 

1765 def fetchall(self) -> Sequence[_R]: 

1766 """A synonym for the :meth:`_engine.ScalarResult.all` method.""" 

1767 

1768 return self._allrows() 

1769 

1770 def fetchmany(self, size: Optional[int] = None) -> Sequence[_R]: 

1771 """Fetch many objects. 

1772 

1773 Equivalent to :meth:`_engine.Result.fetchmany` except that 

1774 scalar values, rather than :class:`_engine.Row` objects, 

1775 are returned. 

1776 

1777 """ 

1778 return self._manyrow_getter(self, size) 

1779 

1780 def all(self) -> Sequence[_R]: 

1781 """Return all scalar values in a sequence. 

1782 

1783 Equivalent to :meth:`_engine.Result.all` except that 

1784 scalar values, rather than :class:`_engine.Row` objects, 

1785 are returned. 

1786 

1787 """ 

1788 return self._allrows() 

1789 

1790 def __iter__(self) -> Iterator[_R]: 

1791 return self._iter_impl() 

1792 

1793 def __next__(self) -> _R: 

1794 return self._next_impl() 

1795 

1796 def first(self) -> Optional[_R]: 

1797 """Fetch the first object or ``None`` if no object is present. 

1798 

1799 Equivalent to :meth:`_engine.Result.first` except that 

1800 scalar values, rather than :class:`_engine.Row` objects, 

1801 are returned. 

1802 

1803 

1804 """ 

1805 return self._only_one_row( 

1806 raise_for_second_row=False, raise_for_none=False, scalar=False 

1807 ) 

1808 

1809 def one_or_none(self) -> Optional[_R]: 

1810 """Return at most one object or raise an exception. 

1811 

1812 Equivalent to :meth:`_engine.Result.one_or_none` except that 

1813 scalar values, rather than :class:`_engine.Row` objects, 

1814 are returned. 

1815 

1816 """ 

1817 return self._only_one_row( 

1818 raise_for_second_row=True, raise_for_none=False, scalar=False 

1819 ) 

1820 

1821 def one(self) -> _R: 

1822 """Return exactly one object or raise an exception. 

1823 

1824 Equivalent to :meth:`_engine.Result.one` except that 

1825 scalar values, rather than :class:`_engine.Row` objects, 

1826 are returned. 

1827 

1828 """ 

1829 return self._only_one_row( 

1830 raise_for_second_row=True, raise_for_none=True, scalar=False 

1831 ) 

1832 

1833 

1834class TupleResult(FilterResult[_R], util.TypingOnly): 

1835 """A :class:`_engine.Result` that's typed as returning plain 

1836 Python tuples instead of rows. 

1837 

1838 Since :class:`_engine.Row` acts like a tuple in every way already, 

1839 this class is a typing only class, regular :class:`_engine.Result` is 

1840 still used at runtime. 

1841 

1842 """ 

1843 

1844 __slots__ = () 

1845 

1846 if TYPE_CHECKING: 

1847 

1848 def partitions( 

1849 self, size: Optional[int] = None 

1850 ) -> Iterator[Sequence[_R]]: 

1851 """Iterate through sub-lists of elements of the size given. 

1852 

1853 Equivalent to :meth:`_engine.Result.partitions` except that 

1854 tuple values, rather than :class:`_engine.Row` objects, 

1855 are returned. 

1856 

1857 """ 

1858 ... 

1859 

1860 def fetchone(self) -> Optional[_R]: 

1861 """Fetch one tuple. 

1862 

1863 Equivalent to :meth:`_engine.Result.fetchone` except that 

1864 tuple values, rather than :class:`_engine.Row` 

1865 objects, are returned. 

1866 

1867 """ 

1868 ... 

1869 

1870 def fetchall(self) -> Sequence[_R]: 

1871 """A synonym for the :meth:`_engine.ScalarResult.all` method.""" 

1872 ... 

1873 

1874 def fetchmany(self, size: Optional[int] = None) -> Sequence[_R]: 

1875 """Fetch many objects. 

1876 

1877 Equivalent to :meth:`_engine.Result.fetchmany` except that 

1878 tuple values, rather than :class:`_engine.Row` objects, 

1879 are returned. 

1880 

1881 """ 

1882 ... 

1883 

1884 def all(self) -> Sequence[_R]: # noqa: A001 

1885 """Return all scalar values in a sequence. 

1886 

1887 Equivalent to :meth:`_engine.Result.all` except that 

1888 tuple values, rather than :class:`_engine.Row` objects, 

1889 are returned. 

1890 

1891 """ 

1892 ... 

1893 

1894 def __iter__(self) -> Iterator[_R]: ... 

1895 

1896 def __next__(self) -> _R: ... 

1897 

1898 def first(self) -> Optional[_R]: 

1899 """Fetch the first object or ``None`` if no object is present. 

1900 

1901 Equivalent to :meth:`_engine.Result.first` except that 

1902 tuple values, rather than :class:`_engine.Row` objects, 

1903 are returned. 

1904 

1905 

1906 """ 

1907 ... 

1908 

1909 def one_or_none(self) -> Optional[_R]: 

1910 """Return at most one object or raise an exception. 

1911 

1912 Equivalent to :meth:`_engine.Result.one_or_none` except that 

1913 tuple values, rather than :class:`_engine.Row` objects, 

1914 are returned. 

1915 

1916 """ 

1917 ... 

1918 

1919 def one(self) -> _R: 

1920 """Return exactly one object or raise an exception. 

1921 

1922 Equivalent to :meth:`_engine.Result.one` except that 

1923 tuple values, rather than :class:`_engine.Row` objects, 

1924 are returned. 

1925 

1926 """ 

1927 ... 

1928 

1929 @overload 

1930 def scalar_one(self: TupleResult[Tuple[_T]]) -> _T: ... 

1931 

1932 @overload 

1933 def scalar_one(self) -> Any: ... 

1934 

1935 def scalar_one(self) -> Any: 

1936 """Return exactly one scalar result or raise an exception. 

1937 

1938 This is equivalent to calling :meth:`_engine.Result.scalars` 

1939 and then :meth:`_engine.ScalarResult.one`. 

1940 

1941 .. seealso:: 

1942 

1943 :meth:`_engine.ScalarResult.one` 

1944 

1945 :meth:`_engine.Result.scalars` 

1946 

1947 """ 

1948 ... 

1949 

1950 @overload 

1951 def scalar_one_or_none( 

1952 self: TupleResult[Tuple[_T]], 

1953 ) -> Optional[_T]: ... 

1954 

1955 @overload 

1956 def scalar_one_or_none(self) -> Optional[Any]: ... 

1957 

1958 def scalar_one_or_none(self) -> Optional[Any]: 

1959 """Return exactly one or no scalar result. 

1960 

1961 This is equivalent to calling :meth:`_engine.Result.scalars` 

1962 and then :meth:`_engine.ScalarResult.one_or_none`. 

1963 

1964 .. seealso:: 

1965 

1966 :meth:`_engine.ScalarResult.one_or_none` 

1967 

1968 :meth:`_engine.Result.scalars` 

1969 

1970 """ 

1971 ... 

1972 

1973 @overload 

1974 def scalar(self: TupleResult[Tuple[_T]]) -> Optional[_T]: ... 

1975 

1976 @overload 

1977 def scalar(self) -> Any: ... 

1978 

1979 def scalar(self) -> Any: 

1980 """Fetch the first column of the first row, and close the result 

1981 set. 

1982 

1983 Returns ``None`` if there are no rows to fetch. 

1984 

1985 No validation is performed to test if additional rows remain. 

1986 

1987 After calling this method, the object is fully closed, 

1988 e.g. the :meth:`_engine.CursorResult.close` 

1989 method will have been called. 

1990 

1991 :return: a Python scalar value , or ``None`` if no rows remain. 

1992 

1993 """ 

1994 ... 

1995 

1996 

1997class MappingResult(_WithKeys, FilterResult[RowMapping]): 

1998 """A wrapper for a :class:`_engine.Result` that returns dictionary values 

1999 rather than :class:`_engine.Row` values. 

2000 

2001 The :class:`_engine.MappingResult` object is acquired by calling the 

2002 :meth:`_engine.Result.mappings` method. 

2003 

2004 """ 

2005 

2006 __slots__ = () 

2007 

2008 _generate_rows = True 

2009 

2010 _post_creational_filter = operator.attrgetter("_mapping") 

2011 

2012 def __init__(self, result: Result[Unpack[TupleAny]]): 

2013 self._real_result = result 

2014 self._unique_filter_state = result._unique_filter_state 

2015 self._metadata = result._metadata 

2016 if result._source_supports_scalars: 

2017 self._metadata = self._metadata._reduce([0]) 

2018 

2019 def unique(self, strategy: Optional[_UniqueFilterType] = None) -> Self: 

2020 """Apply unique filtering to the objects returned by this 

2021 :class:`_engine.MappingResult`. 

2022 

2023 See :meth:`_engine.Result.unique` for usage details. 

2024 

2025 """ 

2026 self._unique_filter_state = (set(), strategy) 

2027 return self 

2028 

2029 def columns(self, *col_expressions: _KeyIndexType) -> Self: 

2030 r"""Establish the columns that should be returned in each row.""" 

2031 return self._column_slices(col_expressions) 

2032 

2033 def partitions( 

2034 self, size: Optional[int] = None 

2035 ) -> Iterator[Sequence[RowMapping]]: 

2036 """Iterate through sub-lists of elements of the size given. 

2037 

2038 Equivalent to :meth:`_engine.Result.partitions` except that 

2039 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

2040 objects, are returned. 

2041 

2042 """ 

2043 

2044 getter = self._manyrow_getter 

2045 

2046 while True: 

2047 partition = getter(self, size) 

2048 if partition: 

2049 yield partition 

2050 else: 

2051 break 

2052 

2053 def fetchall(self) -> Sequence[RowMapping]: 

2054 """A synonym for the :meth:`_engine.MappingResult.all` method.""" 

2055 

2056 return self._allrows() 

2057 

2058 def fetchone(self) -> Optional[RowMapping]: 

2059 """Fetch one object. 

2060 

2061 Equivalent to :meth:`_engine.Result.fetchone` except that 

2062 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

2063 objects, are returned. 

2064 

2065 """ 

2066 

2067 row = self._onerow_getter(self) 

2068 if row is _NO_ROW: 

2069 return None 

2070 else: 

2071 return row 

2072 

2073 def fetchmany(self, size: Optional[int] = None) -> Sequence[RowMapping]: 

2074 """Fetch many objects. 

2075 

2076 Equivalent to :meth:`_engine.Result.fetchmany` except that 

2077 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

2078 objects, are returned. 

2079 

2080 """ 

2081 

2082 return self._manyrow_getter(self, size) 

2083 

2084 def all(self) -> Sequence[RowMapping]: 

2085 """Return all scalar values in a sequence. 

2086 

2087 Equivalent to :meth:`_engine.Result.all` except that 

2088 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

2089 objects, are returned. 

2090 

2091 """ 

2092 

2093 return self._allrows() 

2094 

2095 def __iter__(self) -> Iterator[RowMapping]: 

2096 return self._iter_impl() 

2097 

2098 def __next__(self) -> RowMapping: 

2099 return self._next_impl() 

2100 

2101 def first(self) -> Optional[RowMapping]: 

2102 """Fetch the first object or ``None`` if no object is present. 

2103 

2104 Equivalent to :meth:`_engine.Result.first` except that 

2105 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

2106 objects, are returned. 

2107 

2108 

2109 """ 

2110 return self._only_one_row( 

2111 raise_for_second_row=False, raise_for_none=False, scalar=False 

2112 ) 

2113 

2114 def one_or_none(self) -> Optional[RowMapping]: 

2115 """Return at most one object or raise an exception. 

2116 

2117 Equivalent to :meth:`_engine.Result.one_or_none` except that 

2118 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

2119 objects, are returned. 

2120 

2121 """ 

2122 return self._only_one_row( 

2123 raise_for_second_row=True, raise_for_none=False, scalar=False 

2124 ) 

2125 

2126 def one(self) -> RowMapping: 

2127 """Return exactly one object or raise an exception. 

2128 

2129 Equivalent to :meth:`_engine.Result.one` except that 

2130 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row` 

2131 objects, are returned. 

2132 

2133 """ 

2134 return self._only_one_row( 

2135 raise_for_second_row=True, raise_for_none=True, scalar=False 

2136 ) 

2137 

2138 

2139class FrozenResult(Generic[Unpack[_Ts]]): 

2140 """Represents a :class:`_engine.Result` object in a "frozen" state suitable 

2141 for caching. 

2142 

2143 The :class:`_engine.FrozenResult` object is returned from the 

2144 :meth:`_engine.Result.freeze` method of any :class:`_engine.Result` 

2145 object. 

2146 

2147 A new iterable :class:`_engine.Result` object is generated from a fixed 

2148 set of data each time the :class:`_engine.FrozenResult` is invoked as 

2149 a callable:: 

2150 

2151 

2152 result = connection.execute(query) 

2153 

2154 frozen = result.freeze() 

2155 

2156 unfrozen_result_one = frozen() 

2157 

2158 for row in unfrozen_result_one: 

2159 print(row) 

2160 

2161 unfrozen_result_two = frozen() 

2162 rows = unfrozen_result_two.all() 

2163 

2164 # ... etc 

2165 

2166 .. versionadded:: 1.4 

2167 

2168 .. seealso:: 

2169 

2170 :ref:`do_orm_execute_re_executing` - example usage within the 

2171 ORM to implement a result-set cache. 

2172 

2173 :func:`_orm.loading.merge_frozen_result` - ORM function to merge 

2174 a frozen result back into a :class:`_orm.Session`. 

2175 

2176 """ 

2177 

2178 data: Sequence[Any] 

2179 

2180 def __init__(self, result: Result[Unpack[_Ts]]): 

2181 self.metadata = result._metadata._for_freeze() 

2182 self._source_supports_scalars = result._source_supports_scalars 

2183 self._attributes = result._attributes 

2184 

2185 if self._source_supports_scalars: 

2186 self.data = list(result._raw_row_iterator()) 

2187 else: 

2188 self.data = result.fetchall() 

2189 

2190 def rewrite_rows(self) -> Sequence[Sequence[Any]]: 

2191 if self._source_supports_scalars: 

2192 return [[elem] for elem in self.data] 

2193 else: 

2194 return [list(row) for row in self.data] 

2195 

2196 def with_new_rows( 

2197 self, tuple_data: Sequence[Row[Unpack[_Ts]]] 

2198 ) -> FrozenResult[Unpack[_Ts]]: 

2199 fr = FrozenResult.__new__(FrozenResult) 

2200 fr.metadata = self.metadata 

2201 fr._attributes = self._attributes 

2202 fr._source_supports_scalars = self._source_supports_scalars 

2203 

2204 if self._source_supports_scalars: 

2205 fr.data = [d[0] for d in tuple_data] # type: ignore[misc] 

2206 else: 

2207 fr.data = tuple_data 

2208 return fr 

2209 

2210 def __call__(self) -> Result[Unpack[_Ts]]: 

2211 result: IteratorResult[Unpack[_Ts]] = IteratorResult( 

2212 self.metadata, iter(self.data) 

2213 ) 

2214 result._attributes = self._attributes 

2215 result._source_supports_scalars = self._source_supports_scalars 

2216 return result 

2217 

2218 

2219class IteratorResult(Result[Unpack[_Ts]]): 

2220 """A :class:`_engine.Result` that gets data from a Python iterator of 

2221 :class:`_engine.Row` objects or similar row-like data. 

2222 

2223 .. versionadded:: 1.4 

2224 

2225 """ 

2226 

2227 _hard_closed = False 

2228 _soft_closed = False 

2229 

2230 def __init__( 

2231 self, 

2232 cursor_metadata: ResultMetaData, 

2233 iterator: Iterator[_InterimSupportsScalarsRowType], 

2234 raw: Optional[Result[Any]] = None, 

2235 _source_supports_scalars: bool = False, 

2236 ): 

2237 self._metadata = cursor_metadata 

2238 self.iterator = iterator 

2239 self.raw = raw 

2240 self._source_supports_scalars = _source_supports_scalars 

2241 

2242 @property 

2243 def closed(self) -> bool: 

2244 """Return ``True`` if this :class:`_engine.IteratorResult` has 

2245 been closed 

2246 

2247 .. versionadded:: 1.4.43 

2248 

2249 """ 

2250 return self._hard_closed 

2251 

2252 def _soft_close(self, hard: bool = False, **kw: Any) -> None: 

2253 if hard: 

2254 self._hard_closed = True 

2255 if self.raw is not None: 

2256 self.raw._soft_close(hard=hard, **kw) 

2257 self.iterator = iter([]) 

2258 self._reset_memoizations() 

2259 self._soft_closed = True 

2260 

2261 def _raise_hard_closed(self) -> NoReturn: 

2262 raise exc.ResourceClosedError("This result object is closed.") 

2263 

2264 def _raw_row_iterator(self) -> Iterator[_RowData]: 

2265 return self.iterator 

2266 

2267 def _fetchiter_impl(self) -> Iterator[_InterimSupportsScalarsRowType]: 

2268 if self._hard_closed: 

2269 self._raise_hard_closed() 

2270 return self.iterator 

2271 

2272 def _fetchone_impl( 

2273 self, hard_close: bool = False 

2274 ) -> Optional[_InterimRowType[Row[Unpack[TupleAny]]]]: 

2275 if self._hard_closed: 

2276 self._raise_hard_closed() 

2277 

2278 row = next(self.iterator, _NO_ROW) 

2279 if row is _NO_ROW: 

2280 self._soft_close(hard=hard_close) 

2281 return None 

2282 else: 

2283 return row 

2284 

2285 def _fetchall_impl( 

2286 self, 

2287 ) -> List[_InterimRowType[Row[Unpack[TupleAny]]]]: 

2288 if self._hard_closed: 

2289 self._raise_hard_closed() 

2290 try: 

2291 return list(self.iterator) 

2292 finally: 

2293 self._soft_close() 

2294 

2295 def _fetchmany_impl( 

2296 self, size: Optional[int] = None 

2297 ) -> List[_InterimRowType[Row[Unpack[TupleAny]]]]: 

2298 if self._hard_closed: 

2299 self._raise_hard_closed() 

2300 

2301 return list(itertools.islice(self.iterator, 0, size)) 

2302 

2303 

2304def null_result() -> IteratorResult[Any]: 

2305 return IteratorResult(SimpleResultMetaData([]), iter([])) 

2306 

2307 

2308class ChunkedIteratorResult(IteratorResult[Unpack[_Ts]]): 

2309 """An :class:`_engine.IteratorResult` that works from an 

2310 iterator-producing callable. 

2311 

2312 The given ``chunks`` argument is a function that is given a number of rows 

2313 to return in each chunk, or ``None`` for all rows. The function should 

2314 then return an un-consumed iterator of lists, each list of the requested 

2315 size. 

2316 

2317 The function can be called at any time again, in which case it should 

2318 continue from the same result set but adjust the chunk size as given. 

2319 

2320 .. versionadded:: 1.4 

2321 

2322 """ 

2323 

2324 def __init__( 

2325 self, 

2326 cursor_metadata: ResultMetaData, 

2327 chunks: Callable[ 

2328 [Optional[int]], Iterator[Sequence[_InterimRowType[_R]]] 

2329 ], 

2330 source_supports_scalars: bool = False, 

2331 raw: Optional[Result[Any]] = None, 

2332 dynamic_yield_per: bool = False, 

2333 ): 

2334 self._metadata = cursor_metadata 

2335 self.chunks = chunks 

2336 self._source_supports_scalars = source_supports_scalars 

2337 self.raw = raw 

2338 self.iterator = itertools.chain.from_iterable(self.chunks(None)) 

2339 self.dynamic_yield_per = dynamic_yield_per 

2340 

2341 @_generative 

2342 def yield_per(self, num: int) -> Self: 

2343 # TODO: this throws away the iterator which may be holding 

2344 # onto a chunk. the yield_per cannot be changed once any 

2345 # rows have been fetched. either find a way to enforce this, 

2346 # or we can't use itertools.chain and will instead have to 

2347 # keep track. 

2348 

2349 self._yield_per = num 

2350 self.iterator = itertools.chain.from_iterable(self.chunks(num)) 

2351 return self 

2352 

2353 def _soft_close(self, hard: bool = False, **kw: Any) -> None: 

2354 super()._soft_close(hard=hard, **kw) 

2355 self.chunks = lambda size: [] # type: ignore 

2356 

2357 def _fetchmany_impl( 

2358 self, size: Optional[int] = None 

2359 ) -> List[_InterimRowType[Row[Unpack[TupleAny]]]]: 

2360 if self.dynamic_yield_per: 

2361 self.iterator = itertools.chain.from_iterable(self.chunks(size)) 

2362 return super()._fetchmany_impl(size=size) 

2363 

2364 

2365class MergedResult(IteratorResult[Unpack[_Ts]]): 

2366 """A :class:`_engine.Result` that is merged from any number of 

2367 :class:`_engine.Result` objects. 

2368 

2369 Returned by the :meth:`_engine.Result.merge` method. 

2370 

2371 .. versionadded:: 1.4 

2372 

2373 """ 

2374 

2375 closed = False 

2376 rowcount: Optional[int] 

2377 

2378 def __init__( 

2379 self, 

2380 cursor_metadata: ResultMetaData, 

2381 results: Sequence[Result[Unpack[_Ts]]], 

2382 ): 

2383 self._results = results 

2384 super().__init__( 

2385 cursor_metadata, 

2386 itertools.chain.from_iterable( 

2387 r._raw_row_iterator() for r in results 

2388 ), 

2389 ) 

2390 

2391 self._unique_filter_state = results[0]._unique_filter_state 

2392 self._yield_per = results[0]._yield_per 

2393 

2394 # going to try something w/ this in next rev 

2395 self._source_supports_scalars = results[0]._source_supports_scalars 

2396 

2397 self._attributes = self._attributes.merge_with( 

2398 *[r._attributes for r in results] 

2399 ) 

2400 

2401 def _soft_close(self, hard: bool = False, **kw: Any) -> None: 

2402 for r in self._results: 

2403 r._soft_close(hard=hard, **kw) 

2404 if hard: 

2405 self.closed = True