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 isinstance(key, int): 

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 isinstance(key, int): 

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[self._keys[key] if isinstance(key, int) else key] 

358 for key in keys 

359 ] 

360 except KeyError as ke: 

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

362 

363 indexes: Sequence[int] 

364 new_keys: Sequence[str] 

365 extra: Sequence[Any] 

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

367 

368 if self._translated_indexes: 

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

370 

371 tup = tuplegetter(*indexes) 

372 

373 new_metadata = SimpleResultMetaData( 

374 new_keys, 

375 extra=extra, 

376 _tuplefilter=tup, 

377 _translated_indexes=indexes, 

378 _processors=self._processors, 

379 _unique_filters=self._unique_filters, 

380 ) 

381 

382 return new_metadata 

383 

384 

385def result_tuple( 

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

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

388 parent = SimpleResultMetaData(fields, extra) 

389 return functools.partial( 

390 Row, parent, parent._effective_processors, parent._key_to_index 

391 ) 

392 

393 

394# a symbol that indicates to internal Result methods that 

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

396# filter is applied to rows. 

397class _NoRow(Enum): 

398 _NO_ROW = 0 

399 

400 

401_NO_ROW = _NoRow._NO_ROW 

402 

403 

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

405 __slots__ = () 

406 

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

408 _generate_rows: bool = True 

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

410 

411 _unique_filter_state: Optional[_UniqueFilterStateType] = None 

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

413 _is_cursor = False 

414 

415 _metadata: ResultMetaData 

416 

417 _source_supports_scalars: bool 

418 

419 def _fetchiter_impl( 

420 self, 

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

422 raise NotImplementedError() 

423 

424 def _fetchone_impl( 

425 self, hard_close: bool = False 

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

427 raise NotImplementedError() 

428 

429 def _fetchmany_impl( 

430 self, size: Optional[int] = None 

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

432 raise NotImplementedError() 

433 

434 def _fetchall_impl( 

435 self, 

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

437 raise NotImplementedError() 

438 

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

440 raise NotImplementedError() 

441 

442 @HasMemoized_ro_memoized_attribute 

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

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

445 self._real_result 

446 if self._real_result 

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

448 ) 

449 

450 if real_result._source_supports_scalars: 

451 if not self._generate_rows: 

452 return None 

453 else: 

454 _proc = Row 

455 

456 def process_row( 

457 metadata: ResultMetaData, 

458 processors: Optional[_ProcessorsType], 

459 key_to_index: Dict[_KeyType, int], 

460 scalar_obj: Any, 

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

462 return _proc( 

463 metadata, processors, key_to_index, (scalar_obj,) 

464 ) 

465 

466 else: 

467 process_row = Row # type: ignore 

468 

469 metadata = self._metadata 

470 

471 key_to_index = metadata._key_to_index 

472 processors = metadata._effective_processors 

473 tf = metadata._tuplefilter 

474 

475 if tf and not real_result._source_supports_scalars: 

476 if processors: 

477 processors = tf(processors) 

478 

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

480 process_row, metadata, processors, key_to_index 

481 ) 

482 

483 fixed_tf = tf 

484 

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

486 return _make_row_orig(fixed_tf(row)) 

487 

488 else: 

489 make_row = functools.partial( # type: ignore 

490 process_row, metadata, processors, key_to_index 

491 ) 

492 

493 if real_result._row_logging_fn: 

494 _log_row = real_result._row_logging_fn 

495 _make_row = make_row 

496 

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

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

499 

500 return make_row 

501 

502 @HasMemoized_ro_memoized_attribute 

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

504 make_row = self._row_getter 

505 

506 post_creational_filter = self._post_creational_filter 

507 

508 if self._unique_filter_state: 

509 uniques, strategy = self._unique_strategy 

510 

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

512 for raw_row in self._fetchiter_impl(): 

513 obj: _InterimRowType[Any] = ( 

514 make_row(raw_row) if make_row else raw_row 

515 ) 

516 hashed = strategy(obj) if strategy else obj 

517 if hashed in uniques: 

518 continue 

519 uniques.add(hashed) 

520 if post_creational_filter: 

521 obj = post_creational_filter(obj) 

522 yield obj # type: ignore 

523 

524 else: 

525 

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

527 for raw_row in self._fetchiter_impl(): 

528 row: _InterimRowType[Any] = ( 

529 make_row(raw_row) if make_row else raw_row 

530 ) 

531 if post_creational_filter: 

532 row = post_creational_filter(row) 

533 yield row # type: ignore 

534 

535 return iterrows 

536 

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

538 make_row = self._row_getter 

539 assert make_row is not None 

540 rows = self._fetchall_impl() 

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

542 

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

544 post_creational_filter = self._post_creational_filter 

545 

546 make_row = self._row_getter 

547 

548 rows = self._fetchall_impl() 

549 made_rows: List[_InterimRowType[_R]] 

550 if make_row: 

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

552 else: 

553 made_rows = rows # type: ignore 

554 

555 interim_rows: List[_R] 

556 

557 if self._unique_filter_state: 

558 uniques, strategy = self._unique_strategy 

559 

560 interim_rows = [ 

561 made_row # type: ignore 

562 for made_row, sig_row in [ 

563 ( 

564 made_row, 

565 strategy(made_row) if strategy else made_row, 

566 ) 

567 for made_row in made_rows 

568 ] 

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

570 ] 

571 else: 

572 interim_rows = made_rows # type: ignore 

573 

574 if post_creational_filter: 

575 interim_rows = [ 

576 post_creational_filter(row) for row in interim_rows 

577 ] 

578 return interim_rows 

579 

580 @HasMemoized_ro_memoized_attribute 

581 def _onerow_getter( 

582 self, 

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

584 make_row = self._row_getter 

585 

586 post_creational_filter = self._post_creational_filter 

587 

588 if self._unique_filter_state: 

589 uniques, strategy = self._unique_strategy 

590 

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

592 _onerow = self._fetchone_impl 

593 while True: 

594 row = _onerow() 

595 if row is None: 

596 return _NO_ROW 

597 else: 

598 obj: _InterimRowType[Any] = ( 

599 make_row(row) if make_row else row 

600 ) 

601 hashed = strategy(obj) if strategy else obj 

602 if hashed in uniques: 

603 continue 

604 else: 

605 uniques.add(hashed) 

606 if post_creational_filter: 

607 obj = post_creational_filter(obj) 

608 return obj # type: ignore 

609 

610 else: 

611 

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

613 row = self._fetchone_impl() 

614 if row is None: 

615 return _NO_ROW 

616 else: 

617 interim_row: _InterimRowType[Any] = ( 

618 make_row(row) if make_row else row 

619 ) 

620 if post_creational_filter: 

621 interim_row = post_creational_filter(interim_row) 

622 return interim_row # type: ignore 

623 

624 return onerow 

625 

626 @HasMemoized_ro_memoized_attribute 

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

628 make_row = self._row_getter 

629 

630 post_creational_filter = self._post_creational_filter 

631 

632 if self._unique_filter_state: 

633 uniques, strategy = self._unique_strategy 

634 

635 def filterrows( 

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

637 rows: List[Any], 

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

639 uniques: Set[Any], 

640 ) -> List[_R]: 

641 if make_row: 

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

643 

644 if strategy: 

645 made_rows = ( 

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

647 ) 

648 else: 

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

650 return [ 

651 made_row 

652 for made_row, sig_row in made_rows 

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

654 ] 

655 

656 def manyrows( 

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

658 ) -> List[_R]: 

659 collect: List[_R] = [] 

660 

661 _manyrows = self._fetchmany_impl 

662 

663 if num is None: 

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

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

666 # different DBAPIs / fetch strategies may be different. 

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

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

669 real_result = ( 

670 self._real_result 

671 if self._real_result 

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

673 ) 

674 if real_result._yield_per: 

675 num_required = num = real_result._yield_per 

676 else: 

677 rows = _manyrows(num) 

678 num = len(rows) 

679 assert make_row is not None 

680 collect.extend( 

681 filterrows(make_row, rows, strategy, uniques) 

682 ) 

683 num_required = num - len(collect) 

684 else: 

685 num_required = num 

686 

687 assert num is not None 

688 

689 while num_required: 

690 rows = _manyrows(num_required) 

691 if not rows: 

692 break 

693 

694 collect.extend( 

695 filterrows(make_row, rows, strategy, uniques) 

696 ) 

697 num_required = num - len(collect) 

698 

699 if post_creational_filter: 

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

701 return collect 

702 

703 else: 

704 

705 def manyrows( 

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

707 ) -> List[_R]: 

708 if num is None: 

709 real_result = ( 

710 self._real_result 

711 if self._real_result 

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

713 ) 

714 num = real_result._yield_per 

715 

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

717 if make_row: 

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

719 if post_creational_filter: 

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

721 return rows # type: ignore 

722 

723 return manyrows 

724 

725 @overload 

726 def _only_one_row( 

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

728 raise_for_second_row: bool, 

729 raise_for_none: bool, 

730 scalar: Literal[True], 

731 ) -> _T: ... 

732 

733 @overload 

734 def _only_one_row( 

735 self, 

736 raise_for_second_row: bool, 

737 raise_for_none: Literal[True], 

738 scalar: bool, 

739 ) -> _R: ... 

740 

741 @overload 

742 def _only_one_row( 

743 self, 

744 raise_for_second_row: bool, 

745 raise_for_none: bool, 

746 scalar: bool, 

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

748 

749 def _only_one_row( 

750 self, 

751 raise_for_second_row: bool, 

752 raise_for_none: bool, 

753 scalar: bool, 

754 ) -> Optional[_R]: 

755 onerow = self._fetchone_impl 

756 

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

758 if row is None: 

759 if raise_for_none: 

760 raise exc.NoResultFound( 

761 "No row was found when one was required" 

762 ) 

763 else: 

764 return None 

765 

766 if scalar and self._source_supports_scalars: 

767 self._generate_rows = False 

768 make_row = None 

769 else: 

770 make_row = self._row_getter 

771 

772 try: 

773 row = make_row(row) if make_row else row 

774 except: 

775 self._soft_close(hard=True) 

776 raise 

777 

778 if raise_for_second_row: 

779 if self._unique_filter_state: 

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

781 # consume the entire result :( 

782 uniques, strategy = self._unique_strategy 

783 

784 existing_row_hash = strategy(row) if strategy else row 

785 

786 while True: 

787 next_row: Any = onerow(hard_close=True) 

788 if next_row is None: 

789 next_row = _NO_ROW 

790 break 

791 

792 try: 

793 next_row = make_row(next_row) if make_row else next_row 

794 

795 if strategy: 

796 assert next_row is not _NO_ROW 

797 if existing_row_hash == strategy(next_row): 

798 continue 

799 elif row == next_row: 

800 continue 

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

802 break 

803 except: 

804 self._soft_close(hard=True) 

805 raise 

806 else: 

807 next_row = onerow(hard_close=True) 

808 if next_row is None: 

809 next_row = _NO_ROW 

810 

811 if next_row is not _NO_ROW: 

812 self._soft_close(hard=True) 

813 raise exc.MultipleResultsFound( 

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

815 if raise_for_none 

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

817 "was required" 

818 ) 

819 else: 

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

821 # closed us :) 

822 self._soft_close(hard=True) 

823 

824 if not scalar: 

825 post_creational_filter = self._post_creational_filter 

826 if post_creational_filter: 

827 row = post_creational_filter(row) 

828 

829 if scalar and make_row: 

830 return row[0] # type: ignore 

831 else: 

832 return row # type: ignore 

833 

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

835 return self._iterator_getter(self) 

836 

837 def _next_impl(self) -> _R: 

838 row = self._onerow_getter(self) 

839 if row is _NO_ROW: 

840 raise StopIteration() 

841 else: 

842 return row 

843 

844 @_generative 

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

846 real_result = ( 

847 self._real_result 

848 if self._real_result 

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

850 ) 

851 

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

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

854 

855 assert self._generate_rows 

856 

857 return self 

858 

859 @HasMemoized.memoized_attribute 

860 def _unique_strategy(self) -> _UniqueFilterStateType: 

861 assert self._unique_filter_state is not None 

862 uniques, strategy = self._unique_filter_state 

863 

864 real_result = ( 

865 self._real_result 

866 if self._real_result is not None 

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

868 ) 

869 

870 if not strategy and self._metadata._unique_filters: 

871 if ( 

872 real_result._source_supports_scalars 

873 and not self._generate_rows 

874 ): 

875 strategy = self._metadata._unique_filters[0] 

876 else: 

877 filters = self._metadata._unique_filters 

878 if self._metadata._tuplefilter: 

879 filters = self._metadata._tuplefilter(filters) 

880 

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

882 return uniques, strategy 

883 

884 

885class _WithKeys: 

886 __slots__ = () 

887 

888 _metadata: ResultMetaData 

889 

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

891 def keys(self) -> RMKeyView: 

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

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

894 

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

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

897 execution. 

898 

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

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

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

902 

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

904 plain list. 

905 

906 

907 """ 

908 return self._metadata.keys 

909 

910 

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

912 """Represent a set of database results. 

913 

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

915 completely updated usage model and calling facade for SQLAlchemy 

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

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

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

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

920 is normally used. 

921 

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

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

924 yield instances of ORM mapped objects either individually or within 

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

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

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

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

929 method. 

930 

931 .. seealso:: 

932 

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

934 

935 """ 

936 

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

938 

939 _row_logging_fn: Optional[ 

940 Callable[[Row[Unpack[TupleAny]]], Row[Unpack[TupleAny]]] 

941 ] = None 

942 

943 _source_supports_scalars: bool = False 

944 

945 _yield_per: Optional[int] = None 

946 

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

948 

949 def __init__(self, cursor_metadata: ResultMetaData): 

950 self._metadata = cursor_metadata 

951 

952 def __enter__(self) -> Self: 

953 return self 

954 

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

956 self.close() 

957 

958 def close(self) -> None: 

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

960 

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

962 not implemented by default. The method should generally end 

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

964 subsequent iteration or row fetching to raise 

965 :class:`.ResourceClosedError`. 

966 

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

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

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

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

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

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

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

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

975 exhaust and autoclose the database cursor. 

976 

977 """ 

978 self._soft_close(hard=True) 

979 

980 @property 

981 def _soft_closed(self) -> bool: 

982 raise NotImplementedError() 

983 

984 @property 

985 def closed(self) -> bool: 

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

987 

988 .. versionadded:: 1.4.43 

989 

990 """ 

991 raise NotImplementedError() 

992 

993 @_generative 

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

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

996 

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

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

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

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

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

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

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

1004 rows remain if fewer remain. 

1005 

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

1007 conjunction with the 

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

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

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

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

1012 

1013 .. tip:: 

1014 

1015 Consider using the 

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

1017 execution option, which will simultaneously set 

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

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

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

1021 a fixed row buffer size at once. 

1022 

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

1024 execution option is available for ORM operations, with 

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

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

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

1028 

1029 .. versionadded:: 1.4 

1030 

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

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

1033 

1034 .. seealso:: 

1035 

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

1037 :meth:`_engine.Result.yield_per` 

1038 

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

1040 

1041 """ 

1042 self._yield_per = num 

1043 return self 

1044 

1045 @_generative 

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

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

1048 :class:`_engine.Result`. 

1049 

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

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

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

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

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

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

1056 returned objects. 

1057 

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

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

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

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

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

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

1064 

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

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

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

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

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

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

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

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

1073 of the requested size. 

1074 

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

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

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

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

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

1080 :class:`_engine.Result` object. 

1081 

1082 """ 

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

1084 return self 

1085 

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

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

1088 

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

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

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

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

1093 a given statement construct. 

1094 

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

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

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

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

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

1100 has been corrected such that calling upon 

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

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

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

1104 only a single column. 

1105 

1106 E.g.:: 

1107 

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

1109 result = connection.execute(statement) 

1110 

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

1112 ... 

1113 

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

1115 

1116 for z, y in result.columns( 

1117 statement.selected_columns.c.z, statement.selected_columns.c.y 

1118 ): 

1119 ... 

1120 

1121 .. versionadded:: 1.4 

1122 

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

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

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

1126 

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

1128 given. 

1129 

1130 """ 

1131 return self._column_slices(col_expressions) 

1132 

1133 @overload 

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

1135 

1136 @overload 

1137 def scalars( 

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

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

1140 

1141 @overload 

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

1143 

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

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

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

1147 

1148 E.g.:: 

1149 

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

1151 >>> result.scalars().all() 

1152 [1, 2, 3] 

1153 

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

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

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

1157 

1158 .. versionadded:: 1.4 

1159 

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

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

1162 

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

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

1165 

1166 """ 

1167 return ScalarResult(self, index) 

1168 

1169 def _getter( 

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

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

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

1173 :class:`_engine.Row`. 

1174 

1175 """ 

1176 if self._source_supports_scalars: 

1177 raise NotImplementedError( 

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

1179 ) 

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

1181 

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

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

1184 :class:`_engine.Row`. 

1185 

1186 """ 

1187 if self._source_supports_scalars: 

1188 raise NotImplementedError( 

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

1190 ) 

1191 return self._metadata._row_as_tuple_getter(keys) 

1192 

1193 def mappings(self) -> MappingResult: 

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

1195 :class:`_engine.MappingResult`. 

1196 

1197 When this filter is applied, fetching rows will return 

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

1199 objects. 

1200 

1201 .. versionadded:: 1.4 

1202 

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

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

1205 

1206 """ 

1207 

1208 return MappingResult(self) 

1209 

1210 @property 

1211 @deprecated( 

1212 "2.1.0", 

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

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

1215 ) 

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

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

1218 

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

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

1221 

1222 .. versionadded:: 2.0 

1223 

1224 .. seealso:: 

1225 

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

1227 workaround for SQLAlchemy 2.1. 

1228 

1229 """ 

1230 return self # type: ignore 

1231 

1232 @deprecated( 

1233 "2.1.0", 

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

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

1236 ) 

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

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

1239 

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

1241 at runtime, 

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

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

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

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

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

1247 itself included typing information. 

1248 

1249 .. versionadded:: 2.0 

1250 

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

1252 

1253 .. seealso:: 

1254 

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

1256 workaround for SQLAlchemy 2.1. 

1257 

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

1259 

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

1261 

1262 """ 

1263 

1264 return self # type: ignore 

1265 

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

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

1268 

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

1270 to merge multiple compatible results together. 

1271 

1272 """ 

1273 raise NotImplementedError() 

1274 

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

1276 return self._iter_impl() 

1277 

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

1279 return self._next_impl() 

1280 

1281 def partitions( 

1282 self, size: Optional[int] = None 

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

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

1285 

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

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

1288 lists will be yielded. 

1289 

1290 The result object is automatically closed when the iterator 

1291 is fully consumed. 

1292 

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

1294 ahead of time unless the 

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

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

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

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

1299 

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

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

1302 combined with use of the 

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

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

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

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

1307 yielding them out. 

1308 

1309 .. versionadded:: 1.4 

1310 

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

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

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

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

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

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

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

1318 specific and not well defined. 

1319 

1320 :return: iterator of lists 

1321 

1322 .. seealso:: 

1323 

1324 :ref:`engine_stream_results` 

1325 

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

1327 

1328 """ 

1329 

1330 getter = self._manyrow_getter 

1331 

1332 while True: 

1333 partition = getter(self, size) 

1334 if partition: 

1335 yield partition 

1336 else: 

1337 break 

1338 

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

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

1341 

1342 return self._allrows() 

1343 

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

1345 """Fetch one row. 

1346 

1347 When all rows are exhausted, returns None. 

1348 

1349 This method is provided for backwards compatibility with 

1350 SQLAlchemy 1.x.x. 

1351 

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

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

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

1355 

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

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

1358 

1359 """ 

1360 row = self._onerow_getter(self) 

1361 if row is _NO_ROW: 

1362 return None 

1363 else: 

1364 return row 

1365 

1366 def fetchmany( 

1367 self, size: Optional[int] = None 

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

1369 """Fetch many rows. 

1370 

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

1372 

1373 This method is provided for backwards compatibility with 

1374 SQLAlchemy 1.x.x. 

1375 

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

1377 method. 

1378 

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

1380 

1381 .. seealso:: 

1382 

1383 :meth:`_engine.Result.partitions` 

1384 

1385 """ 

1386 

1387 return self._manyrow_getter(self, size) 

1388 

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

1390 """Return all rows in a sequence. 

1391 

1392 Closes the result set after invocation. Subsequent invocations 

1393 will return an empty sequence. 

1394 

1395 .. versionadded:: 1.4 

1396 

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

1398 

1399 .. seealso:: 

1400 

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

1402 without loading it completely in python. 

1403 

1404 """ 

1405 

1406 return self._allrows() 

1407 

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

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

1410 

1411 Closes the result set and discards remaining rows. 

1412 

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

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

1415 column of the first row, use the 

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

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

1418 :meth:`_engine.Result.first`. 

1419 

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

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

1422 SQL query which was invoked to produce this 

1423 :class:`_engine.Result`; 

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

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

1426 the first row will be discarded. 

1427 

1428 .. seealso:: 

1429 

1430 :ref:`migration_20_unify_select` 

1431 

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

1433 if no rows remain. 

1434 

1435 .. seealso:: 

1436 

1437 :meth:`_engine.Result.scalar` 

1438 

1439 :meth:`_engine.Result.one` 

1440 

1441 """ 

1442 

1443 return self._only_one_row( 

1444 raise_for_second_row=False, raise_for_none=False, scalar=False 

1445 ) 

1446 

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

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

1449 

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

1451 Raises :class:`.MultipleResultsFound` 

1452 if multiple rows are returned. 

1453 

1454 .. versionadded:: 1.4 

1455 

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

1457 is available. 

1458 

1459 :raises: :class:`.MultipleResultsFound` 

1460 

1461 .. seealso:: 

1462 

1463 :meth:`_engine.Result.first` 

1464 

1465 :meth:`_engine.Result.one` 

1466 

1467 """ 

1468 return self._only_one_row( 

1469 raise_for_second_row=True, raise_for_none=False, scalar=False 

1470 ) 

1471 

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

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

1474 

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

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

1477 

1478 .. seealso:: 

1479 

1480 :meth:`_engine.ScalarResult.one` 

1481 

1482 :meth:`_engine.Result.scalars` 

1483 

1484 """ 

1485 return self._only_one_row( 

1486 raise_for_second_row=True, raise_for_none=True, scalar=True 

1487 ) 

1488 

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

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

1491 

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

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

1494 

1495 .. seealso:: 

1496 

1497 :meth:`_engine.ScalarResult.one_or_none` 

1498 

1499 :meth:`_engine.Result.scalars` 

1500 

1501 """ 

1502 return self._only_one_row( 

1503 raise_for_second_row=True, raise_for_none=False, scalar=True 

1504 ) 

1505 

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

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

1508 

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

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

1511 would be returned. 

1512 

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

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

1515 column of the first row, use the 

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

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

1518 :meth:`_engine.Result.one`. 

1519 

1520 .. versionadded:: 1.4 

1521 

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

1523 

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

1525 

1526 .. seealso:: 

1527 

1528 :meth:`_engine.Result.first` 

1529 

1530 :meth:`_engine.Result.one_or_none` 

1531 

1532 :meth:`_engine.Result.scalar_one` 

1533 

1534 """ 

1535 return self._only_one_row( 

1536 raise_for_second_row=True, raise_for_none=True, scalar=False 

1537 ) 

1538 

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

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

1541 

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

1543 

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

1545 

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

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

1548 method will have been called. 

1549 

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

1551 

1552 """ 

1553 return self._only_one_row( 

1554 raise_for_second_row=False, raise_for_none=False, scalar=True 

1555 ) 

1556 

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

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

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

1560 

1561 The callable object returned is an instance of 

1562 :class:`_engine.FrozenResult`. 

1563 

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

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

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

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

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

1569 against its stored set of rows. 

1570 

1571 .. seealso:: 

1572 

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

1574 ORM to implement a result-set cache. 

1575 

1576 """ 

1577 

1578 return FrozenResult(self) 

1579 

1580 def merge( 

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

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

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

1584 objects. 

1585 

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

1587 which will be composed of iterators from the given result 

1588 objects. 

1589 

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

1591 The subsequent result objects must be against an identical 

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

1593 undefined. 

1594 

1595 """ 

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

1597 

1598 

1599class FilterResult(ResultInternal[_R]): 

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

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

1602 

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

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

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

1606 

1607 """ 

1608 

1609 __slots__ = ( 

1610 "_real_result", 

1611 "_post_creational_filter", 

1612 "_metadata", 

1613 "_unique_filter_state", 

1614 "__dict__", 

1615 ) 

1616 

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

1618 

1619 _real_result: Result[Unpack[TupleAny]] 

1620 

1621 def __enter__(self) -> Self: 

1622 return self 

1623 

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

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

1626 

1627 @_generative 

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

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

1630 

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

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

1633 documentation for usage notes. 

1634 

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

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

1637 

1638 .. seealso:: 

1639 

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

1641 :meth:`_engine.Result.yield_per` 

1642 

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

1644 

1645 """ 

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

1647 return self 

1648 

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

1650 self._real_result._soft_close(hard=hard) 

1651 

1652 @property 

1653 def _soft_closed(self) -> bool: 

1654 return self._real_result._soft_closed 

1655 

1656 @property 

1657 def closed(self) -> bool: 

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

1659 closed 

1660 

1661 .. versionadded:: 1.4.43 

1662 

1663 """ 

1664 return self._real_result.closed 

1665 

1666 def close(self) -> None: 

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

1668 

1669 .. versionadded:: 1.4.43 

1670 

1671 """ 

1672 self._real_result.close() 

1673 

1674 @property 

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

1676 return self._real_result._attributes 

1677 

1678 def _fetchiter_impl( 

1679 self, 

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

1681 return self._real_result._fetchiter_impl() 

1682 

1683 def _fetchone_impl( 

1684 self, hard_close: bool = False 

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

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

1687 

1688 def _fetchall_impl( 

1689 self, 

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

1691 return self._real_result._fetchall_impl() 

1692 

1693 def _fetchmany_impl( 

1694 self, size: Optional[int] = None 

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

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

1697 

1698 

1699class ScalarResult(FilterResult[_R]): 

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

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

1702 

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

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

1705 

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

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

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

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

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

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

1712 

1713 """ 

1714 

1715 __slots__ = () 

1716 

1717 _generate_rows = False 

1718 

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

1720 

1721 def __init__( 

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

1723 ): 

1724 self._real_result = real_result 

1725 

1726 if real_result._source_supports_scalars: 

1727 self._metadata = real_result._metadata 

1728 self._post_creational_filter = None 

1729 else: 

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

1731 self._post_creational_filter = operator.itemgetter(0) 

1732 

1733 self._unique_filter_state = real_result._unique_filter_state 

1734 

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

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

1737 :class:`_engine.ScalarResult`. 

1738 

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

1740 

1741 """ 

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

1743 return self 

1744 

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

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

1747 

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

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

1750 are returned. 

1751 

1752 """ 

1753 

1754 getter = self._manyrow_getter 

1755 

1756 while True: 

1757 partition = getter(self, size) 

1758 if partition: 

1759 yield partition 

1760 else: 

1761 break 

1762 

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

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

1765 

1766 return self._allrows() 

1767 

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

1769 """Fetch many objects. 

1770 

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

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

1773 are returned. 

1774 

1775 """ 

1776 return self._manyrow_getter(self, size) 

1777 

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

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

1780 

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

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

1783 are returned. 

1784 

1785 """ 

1786 return self._allrows() 

1787 

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

1789 return self._iter_impl() 

1790 

1791 def __next__(self) -> _R: 

1792 return self._next_impl() 

1793 

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

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

1796 

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

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

1799 are returned. 

1800 

1801 

1802 """ 

1803 return self._only_one_row( 

1804 raise_for_second_row=False, raise_for_none=False, scalar=False 

1805 ) 

1806 

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

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

1809 

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

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

1812 are returned. 

1813 

1814 """ 

1815 return self._only_one_row( 

1816 raise_for_second_row=True, raise_for_none=False, scalar=False 

1817 ) 

1818 

1819 def one(self) -> _R: 

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

1821 

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

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

1824 are returned. 

1825 

1826 """ 

1827 return self._only_one_row( 

1828 raise_for_second_row=True, raise_for_none=True, scalar=False 

1829 ) 

1830 

1831 

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

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

1834 Python tuples instead of rows. 

1835 

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

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

1838 still used at runtime. 

1839 

1840 """ 

1841 

1842 __slots__ = () 

1843 

1844 if TYPE_CHECKING: 

1845 

1846 def partitions( 

1847 self, size: Optional[int] = None 

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

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

1850 

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

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

1853 are returned. 

1854 

1855 """ 

1856 ... 

1857 

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

1859 """Fetch one tuple. 

1860 

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

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

1863 objects, are returned. 

1864 

1865 """ 

1866 ... 

1867 

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

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

1870 ... 

1871 

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

1873 """Fetch many objects. 

1874 

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

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

1877 are returned. 

1878 

1879 """ 

1880 ... 

1881 

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

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

1884 

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

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

1887 are returned. 

1888 

1889 """ 

1890 ... 

1891 

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

1893 

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

1895 

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

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

1898 

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

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

1901 are returned. 

1902 

1903 

1904 """ 

1905 ... 

1906 

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

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

1909 

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

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

1912 are returned. 

1913 

1914 """ 

1915 ... 

1916 

1917 def one(self) -> _R: 

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

1919 

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

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

1922 are returned. 

1923 

1924 """ 

1925 ... 

1926 

1927 @overload 

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

1929 

1930 @overload 

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

1932 

1933 def scalar_one(self) -> Any: 

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

1935 

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

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

1938 

1939 .. seealso:: 

1940 

1941 :meth:`_engine.ScalarResult.one` 

1942 

1943 :meth:`_engine.Result.scalars` 

1944 

1945 """ 

1946 ... 

1947 

1948 @overload 

1949 def scalar_one_or_none( 

1950 self: TupleResult[Tuple[_T]], 

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

1952 

1953 @overload 

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

1955 

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

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

1958 

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

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

1961 

1962 .. seealso:: 

1963 

1964 :meth:`_engine.ScalarResult.one_or_none` 

1965 

1966 :meth:`_engine.Result.scalars` 

1967 

1968 """ 

1969 ... 

1970 

1971 @overload 

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

1973 

1974 @overload 

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

1976 

1977 def scalar(self) -> Any: 

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

1979 set. 

1980 

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

1982 

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

1984 

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

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

1987 method will have been called. 

1988 

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

1990 

1991 """ 

1992 ... 

1993 

1994 

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

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

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

1998 

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

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

2001 

2002 """ 

2003 

2004 __slots__ = () 

2005 

2006 _generate_rows = True 

2007 

2008 _post_creational_filter = operator.attrgetter("_mapping") 

2009 

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

2011 self._real_result = result 

2012 self._unique_filter_state = result._unique_filter_state 

2013 self._metadata = result._metadata 

2014 if result._source_supports_scalars: 

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

2016 

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

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

2019 :class:`_engine.MappingResult`. 

2020 

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

2022 

2023 """ 

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

2025 return self 

2026 

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

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

2029 return self._column_slices(col_expressions) 

2030 

2031 def partitions( 

2032 self, size: Optional[int] = None 

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

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

2035 

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

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

2038 objects, are returned. 

2039 

2040 """ 

2041 

2042 getter = self._manyrow_getter 

2043 

2044 while True: 

2045 partition = getter(self, size) 

2046 if partition: 

2047 yield partition 

2048 else: 

2049 break 

2050 

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

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

2053 

2054 return self._allrows() 

2055 

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

2057 """Fetch one object. 

2058 

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

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

2061 objects, are returned. 

2062 

2063 """ 

2064 

2065 row = self._onerow_getter(self) 

2066 if row is _NO_ROW: 

2067 return None 

2068 else: 

2069 return row 

2070 

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

2072 """Fetch many objects. 

2073 

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

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

2076 objects, are returned. 

2077 

2078 """ 

2079 

2080 return self._manyrow_getter(self, size) 

2081 

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

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

2084 

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

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

2087 objects, are returned. 

2088 

2089 """ 

2090 

2091 return self._allrows() 

2092 

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

2094 return self._iter_impl() 

2095 

2096 def __next__(self) -> RowMapping: 

2097 return self._next_impl() 

2098 

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

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

2101 

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

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

2104 objects, are returned. 

2105 

2106 

2107 """ 

2108 return self._only_one_row( 

2109 raise_for_second_row=False, raise_for_none=False, scalar=False 

2110 ) 

2111 

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

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

2114 

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

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

2117 objects, are returned. 

2118 

2119 """ 

2120 return self._only_one_row( 

2121 raise_for_second_row=True, raise_for_none=False, scalar=False 

2122 ) 

2123 

2124 def one(self) -> RowMapping: 

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

2126 

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

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

2129 objects, are returned. 

2130 

2131 """ 

2132 return self._only_one_row( 

2133 raise_for_second_row=True, raise_for_none=True, scalar=False 

2134 ) 

2135 

2136 

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

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

2139 for caching. 

2140 

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

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

2143 object. 

2144 

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

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

2147 a callable:: 

2148 

2149 

2150 result = connection.execute(query) 

2151 

2152 frozen = result.freeze() 

2153 

2154 unfrozen_result_one = frozen() 

2155 

2156 for row in unfrozen_result_one: 

2157 print(row) 

2158 

2159 unfrozen_result_two = frozen() 

2160 rows = unfrozen_result_two.all() 

2161 

2162 # ... etc 

2163 

2164 .. versionadded:: 1.4 

2165 

2166 .. seealso:: 

2167 

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

2169 ORM to implement a result-set cache. 

2170 

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

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

2173 

2174 """ 

2175 

2176 data: Sequence[Any] 

2177 

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

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

2180 self._source_supports_scalars = result._source_supports_scalars 

2181 self._attributes = result._attributes 

2182 

2183 if self._source_supports_scalars: 

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

2185 else: 

2186 self.data = result.fetchall() 

2187 

2188 def _rewrite_rows(self) -> Sequence[Sequence[Any]]: 

2189 # used only by the orm fn merge_frozen_result 

2190 if self._source_supports_scalars: 

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

2192 else: 

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

2194 

2195 def with_new_rows( 

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

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

2198 fr = FrozenResult.__new__(FrozenResult) 

2199 fr.metadata = self.metadata 

2200 fr._attributes = self._attributes 

2201 fr._source_supports_scalars = self._source_supports_scalars 

2202 

2203 if self._source_supports_scalars: 

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

2205 else: 

2206 fr.data = tuple_data 

2207 return fr 

2208 

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

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

2211 self.metadata, iter(self.data) 

2212 ) 

2213 result._attributes = self._attributes 

2214 result._source_supports_scalars = self._source_supports_scalars 

2215 return result 

2216 

2217 

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

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

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

2221 

2222 .. versionadded:: 1.4 

2223 

2224 """ 

2225 

2226 _hard_closed = False 

2227 _soft_closed = False 

2228 

2229 def __init__( 

2230 self, 

2231 cursor_metadata: ResultMetaData, 

2232 iterator: Iterator[_InterimSupportsScalarsRowType], 

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

2234 _source_supports_scalars: bool = False, 

2235 ): 

2236 self._metadata = cursor_metadata 

2237 self.iterator = iterator 

2238 self.raw = raw 

2239 self._source_supports_scalars = _source_supports_scalars 

2240 

2241 @property 

2242 def closed(self) -> bool: 

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

2244 been closed 

2245 

2246 .. versionadded:: 1.4.43 

2247 

2248 """ 

2249 return self._hard_closed 

2250 

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

2252 if hard: 

2253 self._hard_closed = True 

2254 if self.raw is not None: 

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

2256 self.iterator = iter([]) 

2257 self._reset_memoizations() 

2258 self._soft_closed = True 

2259 

2260 def _raise_hard_closed(self) -> NoReturn: 

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

2262 

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

2264 return self.iterator 

2265 

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

2267 if self._hard_closed: 

2268 self._raise_hard_closed() 

2269 return self.iterator 

2270 

2271 def _fetchone_impl( 

2272 self, hard_close: bool = False 

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

2274 if self._hard_closed: 

2275 self._raise_hard_closed() 

2276 

2277 row = next(self.iterator, _NO_ROW) 

2278 if row is _NO_ROW: 

2279 self._soft_close(hard=hard_close) 

2280 return None 

2281 else: 

2282 return row 

2283 

2284 def _fetchall_impl( 

2285 self, 

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

2287 if self._hard_closed: 

2288 self._raise_hard_closed() 

2289 try: 

2290 return list(self.iterator) 

2291 finally: 

2292 self._soft_close() 

2293 

2294 def _fetchmany_impl( 

2295 self, size: Optional[int] = None 

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

2297 if self._hard_closed: 

2298 self._raise_hard_closed() 

2299 

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

2301 

2302 

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

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

2305 

2306 

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

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

2309 iterator-producing callable. 

2310 

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

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

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

2314 size. 

2315 

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

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

2318 

2319 .. versionadded:: 1.4 

2320 

2321 """ 

2322 

2323 def __init__( 

2324 self, 

2325 cursor_metadata: ResultMetaData, 

2326 chunks: Callable[ 

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

2328 ], 

2329 source_supports_scalars: bool = False, 

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

2331 dynamic_yield_per: bool = False, 

2332 ): 

2333 self._metadata = cursor_metadata 

2334 self.chunks = chunks 

2335 self._source_supports_scalars = source_supports_scalars 

2336 self.raw = raw 

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

2338 self.dynamic_yield_per = dynamic_yield_per 

2339 

2340 @_generative 

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

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

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

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

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

2346 # keep track. 

2347 

2348 self._yield_per = num 

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

2350 return self 

2351 

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

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

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

2355 

2356 def _fetchmany_impl( 

2357 self, size: Optional[int] = None 

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

2359 if self.dynamic_yield_per: 

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

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

2362 

2363 

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

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

2366 :class:`_engine.Result` objects. 

2367 

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

2369 

2370 .. versionadded:: 1.4 

2371 

2372 """ 

2373 

2374 closed = False 

2375 rowcount: Optional[int] 

2376 

2377 def __init__( 

2378 self, 

2379 cursor_metadata: ResultMetaData, 

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

2381 ): 

2382 self._results = results 

2383 super().__init__( 

2384 cursor_metadata, 

2385 itertools.chain.from_iterable( 

2386 r._raw_row_iterator() for r in results 

2387 ), 

2388 ) 

2389 

2390 self._unique_filter_state = results[0]._unique_filter_state 

2391 self._yield_per = results[0]._yield_per 

2392 

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

2394 self._source_supports_scalars = results[0]._source_supports_scalars 

2395 

2396 self._attributes = self._attributes.merge_with( 

2397 *[r._attributes for r in results] 

2398 ) 

2399 

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

2401 for r in self._results: 

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

2403 if hard: 

2404 self.closed = True