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

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

822 statements  

1# engine/result.py 

2# Copyright (C) 2005-2024 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.schema import Column 

55 from ..sql.type_api import _ResultProcessorType 

56 

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

58_KeyIndexType = Union[str, "Column[Any]", 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, 

730 raise_for_second_row: bool, 

731 raise_for_none: Literal[True], 

732 scalar: bool, 

733 ) -> _R: ... 

734 

735 @overload 

736 def _only_one_row( 

737 self, 

738 raise_for_second_row: bool, 

739 raise_for_none: bool, 

740 scalar: bool, 

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

742 

743 def _only_one_row( 

744 self, 

745 raise_for_second_row: bool, 

746 raise_for_none: bool, 

747 scalar: bool, 

748 ) -> Optional[_R]: 

749 onerow = self._fetchone_impl 

750 

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

752 if row is None: 

753 if raise_for_none: 

754 raise exc.NoResultFound( 

755 "No row was found when one was required" 

756 ) 

757 else: 

758 return None 

759 

760 if scalar and self._source_supports_scalars: 

761 self._generate_rows = False 

762 make_row = None 

763 else: 

764 make_row = self._row_getter 

765 

766 try: 

767 row = make_row(row) if make_row else row 

768 except: 

769 self._soft_close(hard=True) 

770 raise 

771 

772 if raise_for_second_row: 

773 if self._unique_filter_state: 

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

775 # consume the entire result :( 

776 uniques, strategy = self._unique_strategy 

777 

778 existing_row_hash = strategy(row) if strategy else row 

779 

780 while True: 

781 next_row: Any = onerow(hard_close=True) 

782 if next_row is None: 

783 next_row = _NO_ROW 

784 break 

785 

786 try: 

787 next_row = make_row(next_row) if make_row else next_row 

788 

789 if strategy: 

790 assert next_row is not _NO_ROW 

791 if existing_row_hash == strategy(next_row): 

792 continue 

793 elif row == next_row: 

794 continue 

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

796 break 

797 except: 

798 self._soft_close(hard=True) 

799 raise 

800 else: 

801 next_row = onerow(hard_close=True) 

802 if next_row is None: 

803 next_row = _NO_ROW 

804 

805 if next_row is not _NO_ROW: 

806 self._soft_close(hard=True) 

807 raise exc.MultipleResultsFound( 

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

809 if raise_for_none 

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

811 "was required" 

812 ) 

813 else: 

814 next_row = _NO_ROW 

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

816 # closed us :) 

817 self._soft_close(hard=True) 

818 

819 if not scalar: 

820 post_creational_filter = self._post_creational_filter 

821 if post_creational_filter: 

822 row = post_creational_filter(row) 

823 

824 if scalar and make_row: 

825 return row[0] # type: ignore 

826 else: 

827 return row # type: ignore 

828 

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

830 return self._iterator_getter(self) 

831 

832 def _next_impl(self) -> _R: 

833 row = self._onerow_getter(self) 

834 if row is _NO_ROW: 

835 raise StopIteration() 

836 else: 

837 return row 

838 

839 @_generative 

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

841 real_result = ( 

842 self._real_result 

843 if self._real_result 

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

845 ) 

846 

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

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

849 

850 assert self._generate_rows 

851 

852 return self 

853 

854 @HasMemoized.memoized_attribute 

855 def _unique_strategy(self) -> _UniqueFilterStateType: 

856 assert self._unique_filter_state is not None 

857 uniques, strategy = self._unique_filter_state 

858 

859 real_result = ( 

860 self._real_result 

861 if self._real_result is not None 

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

863 ) 

864 

865 if not strategy and self._metadata._unique_filters: 

866 if ( 

867 real_result._source_supports_scalars 

868 and not self._generate_rows 

869 ): 

870 strategy = self._metadata._unique_filters[0] 

871 else: 

872 filters = self._metadata._unique_filters 

873 if self._metadata._tuplefilter: 

874 filters = self._metadata._tuplefilter(filters) 

875 

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

877 return uniques, strategy 

878 

879 

880class _WithKeys: 

881 __slots__ = () 

882 

883 _metadata: ResultMetaData 

884 

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

886 def keys(self) -> RMKeyView: 

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

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

889 

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

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

892 execution. 

893 

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

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

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

897 

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

899 plain list. 

900 

901 

902 """ 

903 return self._metadata.keys 

904 

905 

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

907 """Represent a set of database results. 

908 

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

910 completely updated usage model and calling facade for SQLAlchemy 

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

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

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

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

915 is normally used. 

916 

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

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

919 yield instances of ORM mapped objects either individually or within 

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

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

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

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

924 method. 

925 

926 .. seealso:: 

927 

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

929 

930 """ 

931 

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

933 

934 _row_logging_fn: Optional[ 

935 Callable[[Row[Unpack[TupleAny]]], Row[Unpack[TupleAny]]] 

936 ] = None 

937 

938 _source_supports_scalars: bool = False 

939 

940 _yield_per: Optional[int] = None 

941 

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

943 

944 def __init__(self, cursor_metadata: ResultMetaData): 

945 self._metadata = cursor_metadata 

946 

947 def __enter__(self) -> Self: 

948 return self 

949 

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

951 self.close() 

952 

953 def close(self) -> None: 

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

955 

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

957 not implemented by default. The method should generally end 

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

959 subsequent iteration or row fetching to raise 

960 :class:`.ResourceClosedError`. 

961 

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

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

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

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

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

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

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

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

970 exhaust and autoclose the database cursor. 

971 

972 """ 

973 self._soft_close(hard=True) 

974 

975 @property 

976 def _soft_closed(self) -> bool: 

977 raise NotImplementedError() 

978 

979 @property 

980 def closed(self) -> bool: 

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

982 

983 .. versionadded:: 1.4.43 

984 

985 """ 

986 raise NotImplementedError() 

987 

988 @_generative 

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

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

991 

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

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

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

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

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

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

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

999 rows remain if fewer remain. 

1000 

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

1002 conjunction with the 

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

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

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

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

1007 

1008 .. tip:: 

1009 

1010 Consider using the 

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

1012 execution option, which will simultaneously set 

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

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

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

1016 a fixed row buffer size at once. 

1017 

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

1019 execution option is available for ORM operations, with 

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

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

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

1023 

1024 .. versionadded:: 1.4 

1025 

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

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

1028 

1029 .. seealso:: 

1030 

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

1032 :meth:`_engine.Result.yield_per` 

1033 

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

1035 

1036 """ 

1037 self._yield_per = num 

1038 return self 

1039 

1040 @_generative 

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

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

1043 :class:`_engine.Result`. 

1044 

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

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

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

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

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

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

1051 returned objects. 

1052 

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

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

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

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

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

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

1059 

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

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

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

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

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

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

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

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

1068 of the requested size. 

1069 

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

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

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

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

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

1075 :class:`_engine.Result` object. 

1076 

1077 """ 

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

1079 return self 

1080 

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

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

1083 

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

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

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

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

1088 a given statement construct. 

1089 

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

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

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

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

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

1095 has been corrected such that calling upon 

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

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

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

1099 only a single column. 

1100 

1101 E.g.:: 

1102 

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

1104 result = connection.execute(statement) 

1105 

1106 for z, y in result.columns('z', 'y'): 

1107 # ... 

1108 

1109 

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

1111 

1112 for z, y in result.columns( 

1113 statement.selected_columns.c.z, 

1114 statement.selected_columns.c.y 

1115 ): 

1116 # ... 

1117 

1118 .. versionadded:: 1.4 

1119 

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

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

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

1123 

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

1125 given. 

1126 

1127 """ 

1128 return self._column_slices(col_expressions) 

1129 

1130 @overload 

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

1132 

1133 @overload 

1134 def scalars( 

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

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

1137 

1138 @overload 

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

1140 

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

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

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

1144 

1145 E.g.:: 

1146 

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

1148 >>> result.scalars().all() 

1149 [1, 2, 3] 

1150 

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

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

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

1154 

1155 .. versionadded:: 1.4 

1156 

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

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

1159 

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

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

1162 

1163 """ 

1164 return ScalarResult(self, index) 

1165 

1166 def _getter( 

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

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

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

1170 :class:`_engine.Row`. 

1171 

1172 """ 

1173 if self._source_supports_scalars: 

1174 raise NotImplementedError( 

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

1176 ) 

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

1178 

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

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

1181 :class:`_engine.Row`. 

1182 

1183 """ 

1184 if self._source_supports_scalars: 

1185 raise NotImplementedError( 

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

1187 ) 

1188 return self._metadata._row_as_tuple_getter(keys) 

1189 

1190 def mappings(self) -> MappingResult: 

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

1192 :class:`_engine.MappingResult`. 

1193 

1194 When this filter is applied, fetching rows will return 

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

1196 objects. 

1197 

1198 .. versionadded:: 1.4 

1199 

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

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

1202 

1203 """ 

1204 

1205 return MappingResult(self) 

1206 

1207 @property 

1208 @deprecated( 

1209 "2.1.0", 

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

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

1212 ) 

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

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

1215 

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

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

1218 

1219 .. versionadded:: 2.0 

1220 

1221 .. seealso:: 

1222 

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

1224 workaround for SQLAlchemy 2.1. 

1225 

1226 """ 

1227 return self # type: ignore 

1228 

1229 @deprecated( 

1230 "2.1.0", 

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

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

1233 ) 

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

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

1236 

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

1238 at runtime, 

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

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

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

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

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

1244 itself included typing information. 

1245 

1246 .. versionadded:: 2.0 

1247 

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

1249 

1250 .. seealso:: 

1251 

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

1253 workaround for SQLAlchemy 2.1. 

1254 

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

1256 

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

1258 

1259 """ 

1260 

1261 return self # type: ignore 

1262 

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

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

1265 

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

1267 to merge multiple compatible results together. 

1268 

1269 """ 

1270 raise NotImplementedError() 

1271 

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

1273 return self._iter_impl() 

1274 

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

1276 return self._next_impl() 

1277 

1278 def partitions( 

1279 self, size: Optional[int] = None 

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

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

1282 

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

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

1285 lists will be yielded. 

1286 

1287 The result object is automatically closed when the iterator 

1288 is fully consumed. 

1289 

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

1291 ahead of time unless the 

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

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

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

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

1296 

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

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

1299 combined with use of the 

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

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

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

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

1304 yielding them out. 

1305 

1306 .. versionadded:: 1.4 

1307 

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

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

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

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

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

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

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

1315 specific and not well defined. 

1316 

1317 :return: iterator of lists 

1318 

1319 .. seealso:: 

1320 

1321 :ref:`engine_stream_results` 

1322 

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

1324 

1325 """ 

1326 

1327 getter = self._manyrow_getter 

1328 

1329 while True: 

1330 partition = getter(self, size) 

1331 if partition: 

1332 yield partition 

1333 else: 

1334 break 

1335 

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

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

1338 

1339 return self._allrows() 

1340 

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

1342 """Fetch one row. 

1343 

1344 When all rows are exhausted, returns None. 

1345 

1346 This method is provided for backwards compatibility with 

1347 SQLAlchemy 1.x.x. 

1348 

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

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

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

1352 

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

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

1355 

1356 """ 

1357 row = self._onerow_getter(self) 

1358 if row is _NO_ROW: 

1359 return None 

1360 else: 

1361 return row 

1362 

1363 def fetchmany( 

1364 self, size: Optional[int] = None 

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

1366 """Fetch many rows. 

1367 

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

1369 

1370 This method is provided for backwards compatibility with 

1371 SQLAlchemy 1.x.x. 

1372 

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

1374 method. 

1375 

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

1377 

1378 .. seealso:: 

1379 

1380 :meth:`_engine.Result.partitions` 

1381 

1382 """ 

1383 

1384 return self._manyrow_getter(self, size) 

1385 

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

1387 """Return all rows in a sequence. 

1388 

1389 Closes the result set after invocation. Subsequent invocations 

1390 will return an empty sequence. 

1391 

1392 .. versionadded:: 1.4 

1393 

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

1395 

1396 .. seealso:: 

1397 

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

1399 without loading it completely in python. 

1400 

1401 """ 

1402 

1403 return self._allrows() 

1404 

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

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

1407 

1408 Closes the result set and discards remaining rows. 

1409 

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

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

1412 column of the first row, use the 

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

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

1415 :meth:`_engine.Result.first`. 

1416 

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

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

1419 SQL query which was invoked to produce this 

1420 :class:`_engine.Result`; 

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

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

1423 the first row will be discarded. 

1424 

1425 .. seealso:: 

1426 

1427 :ref:`migration_20_unify_select` 

1428 

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

1430 if no rows remain. 

1431 

1432 .. seealso:: 

1433 

1434 :meth:`_engine.Result.scalar` 

1435 

1436 :meth:`_engine.Result.one` 

1437 

1438 """ 

1439 

1440 return self._only_one_row( 

1441 raise_for_second_row=False, raise_for_none=False, scalar=False 

1442 ) 

1443 

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

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

1446 

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

1448 Raises :class:`.MultipleResultsFound` 

1449 if multiple rows are returned. 

1450 

1451 .. versionadded:: 1.4 

1452 

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

1454 is available. 

1455 

1456 :raises: :class:`.MultipleResultsFound` 

1457 

1458 .. seealso:: 

1459 

1460 :meth:`_engine.Result.first` 

1461 

1462 :meth:`_engine.Result.one` 

1463 

1464 """ 

1465 return self._only_one_row( 

1466 raise_for_second_row=True, raise_for_none=False, scalar=False 

1467 ) 

1468 

1469 @overload 

1470 def scalar_one(self: Result[_T]) -> _T: ... 

1471 

1472 @overload 

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

1474 

1475 def scalar_one(self) -> Any: 

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

1477 

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

1479 then :meth:`_engine.Result.one`. 

1480 

1481 .. seealso:: 

1482 

1483 :meth:`_engine.Result.one` 

1484 

1485 :meth:`_engine.Result.scalars` 

1486 

1487 """ 

1488 return self._only_one_row( 

1489 raise_for_second_row=True, raise_for_none=True, scalar=True 

1490 ) 

1491 

1492 @overload 

1493 def scalar_one_or_none(self: Result[_T]) -> Optional[_T]: ... 

1494 

1495 @overload 

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

1497 

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

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

1500 

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

1502 then :meth:`_engine.Result.one_or_none`. 

1503 

1504 .. seealso:: 

1505 

1506 :meth:`_engine.Result.one_or_none` 

1507 

1508 :meth:`_engine.Result.scalars` 

1509 

1510 """ 

1511 return self._only_one_row( 

1512 raise_for_second_row=True, raise_for_none=False, scalar=True 

1513 ) 

1514 

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

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

1517 

1518 Raises :class:`.NoResultFound` if the result returns no 

1519 rows, or :class:`.MultipleResultsFound` if multiple rows 

1520 would be returned. 

1521 

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

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

1524 column of the first row, use the 

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

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

1527 :meth:`_engine.Result.one`. 

1528 

1529 .. versionadded:: 1.4 

1530 

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

1532 

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

1534 

1535 .. seealso:: 

1536 

1537 :meth:`_engine.Result.first` 

1538 

1539 :meth:`_engine.Result.one_or_none` 

1540 

1541 :meth:`_engine.Result.scalar_one` 

1542 

1543 """ 

1544 return self._only_one_row( 

1545 raise_for_second_row=True, raise_for_none=True, scalar=False 

1546 ) 

1547 

1548 @overload 

1549 def scalar(self: Result[_T]) -> Optional[_T]: ... 

1550 

1551 @overload 

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

1553 

1554 def scalar(self) -> Any: 

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

1556 

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

1558 

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

1560 

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

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

1563 method will have been called. 

1564 

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

1566 

1567 """ 

1568 return self._only_one_row( 

1569 raise_for_second_row=False, raise_for_none=False, scalar=True 

1570 ) 

1571 

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

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

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

1575 

1576 The callable object returned is an instance of 

1577 :class:`_engine.FrozenResult`. 

1578 

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

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

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

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

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

1584 against its stored set of rows. 

1585 

1586 .. seealso:: 

1587 

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

1589 ORM to implement a result-set cache. 

1590 

1591 """ 

1592 

1593 return FrozenResult(self) 

1594 

1595 def merge( 

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

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

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

1599 objects. 

1600 

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

1602 which will be composed of iterators from the given result 

1603 objects. 

1604 

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

1606 The subsequent result objects must be against an identical 

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

1608 undefined. 

1609 

1610 """ 

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

1612 

1613 

1614class FilterResult(ResultInternal[_R]): 

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

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

1617 

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

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

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

1621 

1622 """ 

1623 

1624 __slots__ = ( 

1625 "_real_result", 

1626 "_post_creational_filter", 

1627 "_metadata", 

1628 "_unique_filter_state", 

1629 "__dict__", 

1630 ) 

1631 

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

1633 

1634 _real_result: Result[Unpack[TupleAny]] 

1635 

1636 def __enter__(self) -> Self: 

1637 return self 

1638 

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

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

1641 

1642 @_generative 

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

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

1645 

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

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

1648 documentation for usage notes. 

1649 

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

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

1652 

1653 .. seealso:: 

1654 

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

1656 :meth:`_engine.Result.yield_per` 

1657 

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

1659 

1660 """ 

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

1662 return self 

1663 

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

1665 self._real_result._soft_close(hard=hard) 

1666 

1667 @property 

1668 def _soft_closed(self) -> bool: 

1669 return self._real_result._soft_closed 

1670 

1671 @property 

1672 def closed(self) -> bool: 

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

1674 closed 

1675 

1676 .. versionadded:: 1.4.43 

1677 

1678 """ 

1679 return self._real_result.closed 

1680 

1681 def close(self) -> None: 

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

1683 

1684 .. versionadded:: 1.4.43 

1685 

1686 """ 

1687 self._real_result.close() 

1688 

1689 @property 

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

1691 return self._real_result._attributes 

1692 

1693 def _fetchiter_impl( 

1694 self, 

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

1696 return self._real_result._fetchiter_impl() 

1697 

1698 def _fetchone_impl( 

1699 self, hard_close: bool = False 

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

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

1702 

1703 def _fetchall_impl( 

1704 self, 

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

1706 return self._real_result._fetchall_impl() 

1707 

1708 def _fetchmany_impl( 

1709 self, size: Optional[int] = None 

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

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

1712 

1713 

1714class ScalarResult(FilterResult[_R]): 

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

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

1717 

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

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

1720 

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

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

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

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

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

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

1727 

1728 """ 

1729 

1730 __slots__ = () 

1731 

1732 _generate_rows = False 

1733 

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

1735 

1736 def __init__( 

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

1738 ): 

1739 self._real_result = real_result 

1740 

1741 if real_result._source_supports_scalars: 

1742 self._metadata = real_result._metadata 

1743 self._post_creational_filter = None 

1744 else: 

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

1746 self._post_creational_filter = operator.itemgetter(0) 

1747 

1748 self._unique_filter_state = real_result._unique_filter_state 

1749 

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

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

1752 :class:`_engine.ScalarResult`. 

1753 

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

1755 

1756 """ 

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

1758 return self 

1759 

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

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

1762 

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

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

1765 are returned. 

1766 

1767 """ 

1768 

1769 getter = self._manyrow_getter 

1770 

1771 while True: 

1772 partition = getter(self, size) 

1773 if partition: 

1774 yield partition 

1775 else: 

1776 break 

1777 

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

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

1780 

1781 return self._allrows() 

1782 

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

1784 """Fetch many objects. 

1785 

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

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

1788 are returned. 

1789 

1790 """ 

1791 return self._manyrow_getter(self, size) 

1792 

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

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

1795 

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

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

1798 are returned. 

1799 

1800 """ 

1801 return self._allrows() 

1802 

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

1804 return self._iter_impl() 

1805 

1806 def __next__(self) -> _R: 

1807 return self._next_impl() 

1808 

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

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

1811 

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

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

1814 are returned. 

1815 

1816 

1817 """ 

1818 return self._only_one_row( 

1819 raise_for_second_row=False, raise_for_none=False, scalar=False 

1820 ) 

1821 

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

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

1824 

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

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

1827 are returned. 

1828 

1829 """ 

1830 return self._only_one_row( 

1831 raise_for_second_row=True, raise_for_none=False, scalar=False 

1832 ) 

1833 

1834 def one(self) -> _R: 

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

1836 

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

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

1839 are returned. 

1840 

1841 """ 

1842 return self._only_one_row( 

1843 raise_for_second_row=True, raise_for_none=True, scalar=False 

1844 ) 

1845 

1846 

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

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

1849 Python tuples instead of rows. 

1850 

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

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

1853 still used at runtime. 

1854 

1855 """ 

1856 

1857 __slots__ = () 

1858 

1859 if TYPE_CHECKING: 

1860 

1861 def partitions( 

1862 self, size: Optional[int] = None 

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

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

1865 

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

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

1868 are returned. 

1869 

1870 """ 

1871 ... 

1872 

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

1874 """Fetch one tuple. 

1875 

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

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

1878 objects, are returned. 

1879 

1880 """ 

1881 ... 

1882 

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

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

1885 ... 

1886 

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

1888 """Fetch many objects. 

1889 

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

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

1892 are returned. 

1893 

1894 """ 

1895 ... 

1896 

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

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

1899 

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

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

1902 are returned. 

1903 

1904 """ 

1905 ... 

1906 

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

1908 

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

1910 

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

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

1913 

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

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

1916 are returned. 

1917 

1918 

1919 """ 

1920 ... 

1921 

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

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

1924 

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

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

1927 are returned. 

1928 

1929 """ 

1930 ... 

1931 

1932 def one(self) -> _R: 

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

1934 

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

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

1937 are returned. 

1938 

1939 """ 

1940 ... 

1941 

1942 @overload 

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

1944 

1945 @overload 

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

1947 

1948 def scalar_one(self) -> Any: 

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

1950 

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

1952 and then :meth:`_engine.Result.one`. 

1953 

1954 .. seealso:: 

1955 

1956 :meth:`_engine.Result.one` 

1957 

1958 :meth:`_engine.Result.scalars` 

1959 

1960 """ 

1961 ... 

1962 

1963 @overload 

1964 def scalar_one_or_none( 

1965 self: TupleResult[Tuple[_T]], 

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

1967 

1968 @overload 

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

1970 

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

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

1973 

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

1975 and then :meth:`_engine.Result.one_or_none`. 

1976 

1977 .. seealso:: 

1978 

1979 :meth:`_engine.Result.one_or_none` 

1980 

1981 :meth:`_engine.Result.scalars` 

1982 

1983 """ 

1984 ... 

1985 

1986 @overload 

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

1988 

1989 @overload 

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

1991 

1992 def scalar(self) -> Any: 

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

1994 set. 

1995 

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

1997 

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

1999 

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

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

2002 method will have been called. 

2003 

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

2005 

2006 """ 

2007 ... 

2008 

2009 

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

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

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

2013 

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

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

2016 

2017 """ 

2018 

2019 __slots__ = () 

2020 

2021 _generate_rows = True 

2022 

2023 _post_creational_filter = operator.attrgetter("_mapping") 

2024 

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

2026 self._real_result = result 

2027 self._unique_filter_state = result._unique_filter_state 

2028 self._metadata = result._metadata 

2029 if result._source_supports_scalars: 

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

2031 

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

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

2034 :class:`_engine.MappingResult`. 

2035 

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

2037 

2038 """ 

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

2040 return self 

2041 

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

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

2044 return self._column_slices(col_expressions) 

2045 

2046 def partitions( 

2047 self, size: Optional[int] = None 

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

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

2050 

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

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

2053 objects, are returned. 

2054 

2055 """ 

2056 

2057 getter = self._manyrow_getter 

2058 

2059 while True: 

2060 partition = getter(self, size) 

2061 if partition: 

2062 yield partition 

2063 else: 

2064 break 

2065 

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

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

2068 

2069 return self._allrows() 

2070 

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

2072 """Fetch one object. 

2073 

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

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

2076 objects, are returned. 

2077 

2078 """ 

2079 

2080 row = self._onerow_getter(self) 

2081 if row is _NO_ROW: 

2082 return None 

2083 else: 

2084 return row 

2085 

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

2087 """Fetch many objects. 

2088 

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

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

2091 objects, are returned. 

2092 

2093 """ 

2094 

2095 return self._manyrow_getter(self, size) 

2096 

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

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

2099 

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

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

2102 objects, are returned. 

2103 

2104 """ 

2105 

2106 return self._allrows() 

2107 

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

2109 return self._iter_impl() 

2110 

2111 def __next__(self) -> RowMapping: 

2112 return self._next_impl() 

2113 

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

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

2116 

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

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

2119 objects, are returned. 

2120 

2121 

2122 """ 

2123 return self._only_one_row( 

2124 raise_for_second_row=False, raise_for_none=False, scalar=False 

2125 ) 

2126 

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

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

2129 

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

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

2132 objects, are returned. 

2133 

2134 """ 

2135 return self._only_one_row( 

2136 raise_for_second_row=True, raise_for_none=False, scalar=False 

2137 ) 

2138 

2139 def one(self) -> RowMapping: 

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

2141 

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

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

2144 objects, are returned. 

2145 

2146 """ 

2147 return self._only_one_row( 

2148 raise_for_second_row=True, raise_for_none=True, scalar=False 

2149 ) 

2150 

2151 

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

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

2154 for caching. 

2155 

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

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

2158 object. 

2159 

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

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

2162 a callable:: 

2163 

2164 

2165 result = connection.execute(query) 

2166 

2167 frozen = result.freeze() 

2168 

2169 unfrozen_result_one = frozen() 

2170 

2171 for row in unfrozen_result_one: 

2172 print(row) 

2173 

2174 unfrozen_result_two = frozen() 

2175 rows = unfrozen_result_two.all() 

2176 

2177 # ... etc 

2178 

2179 .. versionadded:: 1.4 

2180 

2181 .. seealso:: 

2182 

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

2184 ORM to implement a result-set cache. 

2185 

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

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

2188 

2189 """ 

2190 

2191 data: Sequence[Any] 

2192 

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

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

2195 self._source_supports_scalars = result._source_supports_scalars 

2196 self._attributes = result._attributes 

2197 

2198 if self._source_supports_scalars: 

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

2200 else: 

2201 self.data = result.fetchall() 

2202 

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

2204 if self._source_supports_scalars: 

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

2206 else: 

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

2208 

2209 def with_new_rows( 

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

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

2212 fr = FrozenResult.__new__(FrozenResult) 

2213 fr.metadata = self.metadata 

2214 fr._attributes = self._attributes 

2215 fr._source_supports_scalars = self._source_supports_scalars 

2216 

2217 if self._source_supports_scalars: 

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

2219 else: 

2220 fr.data = tuple_data 

2221 return fr 

2222 

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

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

2225 self.metadata, iter(self.data) 

2226 ) 

2227 result._attributes = self._attributes 

2228 result._source_supports_scalars = self._source_supports_scalars 

2229 return result 

2230 

2231 

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

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

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

2235 

2236 .. versionadded:: 1.4 

2237 

2238 """ 

2239 

2240 _hard_closed = False 

2241 _soft_closed = False 

2242 

2243 def __init__( 

2244 self, 

2245 cursor_metadata: ResultMetaData, 

2246 iterator: Iterator[_InterimSupportsScalarsRowType], 

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

2248 _source_supports_scalars: bool = False, 

2249 ): 

2250 self._metadata = cursor_metadata 

2251 self.iterator = iterator 

2252 self.raw = raw 

2253 self._source_supports_scalars = _source_supports_scalars 

2254 

2255 @property 

2256 def closed(self) -> bool: 

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

2258 been closed 

2259 

2260 .. versionadded:: 1.4.43 

2261 

2262 """ 

2263 return self._hard_closed 

2264 

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

2266 if hard: 

2267 self._hard_closed = True 

2268 if self.raw is not None: 

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

2270 self.iterator = iter([]) 

2271 self._reset_memoizations() 

2272 self._soft_closed = True 

2273 

2274 def _raise_hard_closed(self) -> NoReturn: 

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

2276 

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

2278 return self.iterator 

2279 

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

2281 if self._hard_closed: 

2282 self._raise_hard_closed() 

2283 return self.iterator 

2284 

2285 def _fetchone_impl( 

2286 self, hard_close: bool = False 

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

2288 if self._hard_closed: 

2289 self._raise_hard_closed() 

2290 

2291 row = next(self.iterator, _NO_ROW) 

2292 if row is _NO_ROW: 

2293 self._soft_close(hard=hard_close) 

2294 return None 

2295 else: 

2296 return row 

2297 

2298 def _fetchall_impl( 

2299 self, 

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

2301 if self._hard_closed: 

2302 self._raise_hard_closed() 

2303 try: 

2304 return list(self.iterator) 

2305 finally: 

2306 self._soft_close() 

2307 

2308 def _fetchmany_impl( 

2309 self, size: Optional[int] = None 

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

2311 if self._hard_closed: 

2312 self._raise_hard_closed() 

2313 

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

2315 

2316 

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

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

2319 

2320 

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

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

2323 iterator-producing callable. 

2324 

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

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

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

2328 size. 

2329 

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

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

2332 

2333 .. versionadded:: 1.4 

2334 

2335 """ 

2336 

2337 def __init__( 

2338 self, 

2339 cursor_metadata: ResultMetaData, 

2340 chunks: Callable[ 

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

2342 ], 

2343 source_supports_scalars: bool = False, 

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

2345 dynamic_yield_per: bool = False, 

2346 ): 

2347 self._metadata = cursor_metadata 

2348 self.chunks = chunks 

2349 self._source_supports_scalars = source_supports_scalars 

2350 self.raw = raw 

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

2352 self.dynamic_yield_per = dynamic_yield_per 

2353 

2354 @_generative 

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

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

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

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

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

2360 # keep track. 

2361 

2362 self._yield_per = num 

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

2364 return self 

2365 

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

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

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

2369 

2370 def _fetchmany_impl( 

2371 self, size: Optional[int] = None 

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

2373 if self.dynamic_yield_per: 

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

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

2376 

2377 

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

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

2380 :class:`_engine.Result` objects. 

2381 

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

2383 

2384 .. versionadded:: 1.4 

2385 

2386 """ 

2387 

2388 closed = False 

2389 rowcount: Optional[int] 

2390 

2391 def __init__( 

2392 self, 

2393 cursor_metadata: ResultMetaData, 

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

2395 ): 

2396 self._results = results 

2397 super().__init__( 

2398 cursor_metadata, 

2399 itertools.chain.from_iterable( 

2400 r._raw_row_iterator() for r in results 

2401 ), 

2402 ) 

2403 

2404 self._unique_filter_state = results[0]._unique_filter_state 

2405 self._yield_per = results[0]._yield_per 

2406 

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

2408 self._source_supports_scalars = results[0]._source_supports_scalars 

2409 

2410 self._attributes = self._attributes.merge_with( 

2411 *[r._attributes for r in results] 

2412 ) 

2413 

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

2415 for r in self._results: 

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

2417 if hard: 

2418 self.closed = True