SQLAlchemy 2.1.0rc1__cp315-cp315-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqlalchemy/__init__.py +299 -0
- sqlalchemy/connectors/__init__.py +18 -0
- sqlalchemy/connectors/aioodbc.py +171 -0
- sqlalchemy/connectors/asyncio.py +476 -0
- sqlalchemy/connectors/pyodbc.py +248 -0
- sqlalchemy/dialects/__init__.py +62 -0
- sqlalchemy/dialects/_typing.py +29 -0
- sqlalchemy/dialects/mssql/__init__.py +88 -0
- sqlalchemy/dialects/mssql/aioodbc.py +63 -0
- sqlalchemy/dialects/mssql/base.py +4833 -0
- sqlalchemy/dialects/mssql/information_schema.py +345 -0
- sqlalchemy/dialects/mssql/json.py +140 -0
- sqlalchemy/dialects/mssql/mssqlpython.py +242 -0
- sqlalchemy/dialects/mssql/provision.py +196 -0
- sqlalchemy/dialects/mssql/pymssql.py +130 -0
- sqlalchemy/dialects/mssql/pyodbc.py +697 -0
- sqlalchemy/dialects/mysql/__init__.py +106 -0
- sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
- sqlalchemy/dialects/mysql/aiomysql.py +260 -0
- sqlalchemy/dialects/mysql/asyncmy.py +241 -0
- sqlalchemy/dialects/mysql/base.py +3896 -0
- sqlalchemy/dialects/mysql/cymysql.py +107 -0
- sqlalchemy/dialects/mysql/dml.py +279 -0
- sqlalchemy/dialects/mysql/enumerated.py +277 -0
- sqlalchemy/dialects/mysql/expression.py +146 -0
- sqlalchemy/dialects/mysql/json.py +92 -0
- sqlalchemy/dialects/mysql/mariadb.py +67 -0
- sqlalchemy/dialects/mysql/mariadbconnector.py +314 -0
- sqlalchemy/dialects/mysql/mysqlconnector.py +291 -0
- sqlalchemy/dialects/mysql/mysqldb.py +318 -0
- sqlalchemy/dialects/mysql/provision.py +153 -0
- sqlalchemy/dialects/mysql/pymysql.py +188 -0
- sqlalchemy/dialects/mysql/pyodbc.py +157 -0
- sqlalchemy/dialects/mysql/reflection.py +724 -0
- sqlalchemy/dialects/mysql/reserved_words.py +570 -0
- sqlalchemy/dialects/mysql/types.py +845 -0
- sqlalchemy/dialects/oracle/__init__.py +85 -0
- sqlalchemy/dialects/oracle/base.py +3847 -0
- sqlalchemy/dialects/oracle/cx_oracle.py +1736 -0
- sqlalchemy/dialects/oracle/dictionary.py +507 -0
- sqlalchemy/dialects/oracle/json.py +157 -0
- sqlalchemy/dialects/oracle/oracledb.py +898 -0
- sqlalchemy/dialects/oracle/provision.py +288 -0
- sqlalchemy/dialects/oracle/types.py +367 -0
- sqlalchemy/dialects/oracle/vector.py +366 -0
- sqlalchemy/dialects/postgresql/__init__.py +170 -0
- sqlalchemy/dialects/postgresql/_psycopg_common.py +232 -0
- sqlalchemy/dialects/postgresql/array.py +534 -0
- sqlalchemy/dialects/postgresql/asyncpg.py +1318 -0
- sqlalchemy/dialects/postgresql/base.py +5935 -0
- sqlalchemy/dialects/postgresql/bitstring.py +327 -0
- sqlalchemy/dialects/postgresql/dml.py +360 -0
- sqlalchemy/dialects/postgresql/ext.py +599 -0
- sqlalchemy/dialects/postgresql/hstore.py +422 -0
- sqlalchemy/dialects/postgresql/json.py +411 -0
- sqlalchemy/dialects/postgresql/named_types.py +535 -0
- sqlalchemy/dialects/postgresql/operators.py +129 -0
- sqlalchemy/dialects/postgresql/pg8000.py +655 -0
- sqlalchemy/dialects/postgresql/pg_catalog.py +345 -0
- sqlalchemy/dialects/postgresql/provision.py +202 -0
- sqlalchemy/dialects/postgresql/psycopg.py +800 -0
- sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
- sqlalchemy/dialects/postgresql/psycopg2cffi.py +62 -0
- sqlalchemy/dialects/postgresql/ranges.py +1002 -0
- sqlalchemy/dialects/postgresql/types.py +388 -0
- sqlalchemy/dialects/sqlite/__init__.py +59 -0
- sqlalchemy/dialects/sqlite/aiosqlite.py +375 -0
- sqlalchemy/dialects/sqlite/base.py +3103 -0
- sqlalchemy/dialects/sqlite/dml.py +314 -0
- sqlalchemy/dialects/sqlite/json.py +134 -0
- sqlalchemy/dialects/sqlite/provision.py +237 -0
- sqlalchemy/dialects/sqlite/pysqlcipher.py +166 -0
- sqlalchemy/dialects/sqlite/pysqlite.py +959 -0
- sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
- sqlalchemy/engine/__init__.py +62 -0
- sqlalchemy/engine/_processors_cy.cp315-win_amd64.pyd +0 -0
- sqlalchemy/engine/_processors_cy.py +92 -0
- sqlalchemy/engine/_result_cy.cp315-win_amd64.pyd +0 -0
- sqlalchemy/engine/_result_cy.py +711 -0
- sqlalchemy/engine/_row_cy.cp315-win_amd64.pyd +0 -0
- sqlalchemy/engine/_row_cy.py +232 -0
- sqlalchemy/engine/_util_cy.cp315-win_amd64.pyd +0 -0
- sqlalchemy/engine/_util_cy.py +136 -0
- sqlalchemy/engine/base.py +3357 -0
- sqlalchemy/engine/characteristics.py +155 -0
- sqlalchemy/engine/create.py +877 -0
- sqlalchemy/engine/cursor.py +2425 -0
- sqlalchemy/engine/default.py +2627 -0
- sqlalchemy/engine/events.py +965 -0
- sqlalchemy/engine/interfaces.py +3636 -0
- sqlalchemy/engine/mock.py +133 -0
- sqlalchemy/engine/processors.py +83 -0
- sqlalchemy/engine/reflection.py +2141 -0
- sqlalchemy/engine/result.py +2012 -0
- sqlalchemy/engine/row.py +397 -0
- sqlalchemy/engine/strategies.py +16 -0
- sqlalchemy/engine/url.py +922 -0
- sqlalchemy/engine/util.py +164 -0
- sqlalchemy/event/__init__.py +26 -0
- sqlalchemy/event/api.py +220 -0
- sqlalchemy/event/attr.py +675 -0
- sqlalchemy/event/base.py +473 -0
- sqlalchemy/event/legacy.py +259 -0
- sqlalchemy/event/registry.py +391 -0
- sqlalchemy/events.py +17 -0
- sqlalchemy/exc.py +939 -0
- sqlalchemy/ext/__init__.py +10 -0
- sqlalchemy/ext/associationproxy.py +2073 -0
- sqlalchemy/ext/asyncio/__init__.py +29 -0
- sqlalchemy/ext/asyncio/base.py +281 -0
- sqlalchemy/ext/asyncio/engine.py +1487 -0
- sqlalchemy/ext/asyncio/exc.py +21 -0
- sqlalchemy/ext/asyncio/result.py +994 -0
- sqlalchemy/ext/asyncio/scoping.py +1679 -0
- sqlalchemy/ext/asyncio/session.py +2006 -0
- sqlalchemy/ext/automap.py +1702 -0
- sqlalchemy/ext/baked.py +558 -0
- sqlalchemy/ext/compiler.py +601 -0
- sqlalchemy/ext/declarative/__init__.py +65 -0
- sqlalchemy/ext/declarative/extensions.py +561 -0
- sqlalchemy/ext/horizontal_shard.py +481 -0
- sqlalchemy/ext/hybrid.py +1877 -0
- sqlalchemy/ext/indexable.py +364 -0
- sqlalchemy/ext/instrumentation.py +450 -0
- sqlalchemy/ext/mutable.py +1081 -0
- sqlalchemy/ext/orderinglist.py +440 -0
- sqlalchemy/ext/serializer.py +184 -0
- sqlalchemy/future/__init__.py +17 -0
- sqlalchemy/future/engine.py +15 -0
- sqlalchemy/inspection.py +188 -0
- sqlalchemy/log.py +279 -0
- sqlalchemy/orm/__init__.py +176 -0
- sqlalchemy/orm/_orm_constructors.py +2694 -0
- sqlalchemy/orm/_typing.py +180 -0
- sqlalchemy/orm/attributes.py +2868 -0
- sqlalchemy/orm/base.py +991 -0
- sqlalchemy/orm/bulk_persistence.py +2168 -0
- sqlalchemy/orm/clsregistry.py +630 -0
- sqlalchemy/orm/collections.py +1569 -0
- sqlalchemy/orm/context.py +3475 -0
- sqlalchemy/orm/decl_api.py +2283 -0
- sqlalchemy/orm/decl_base.py +2320 -0
- sqlalchemy/orm/dependency.py +1306 -0
- sqlalchemy/orm/descriptor_props.py +1183 -0
- sqlalchemy/orm/dynamic.py +306 -0
- sqlalchemy/orm/evaluator.py +378 -0
- sqlalchemy/orm/events.py +3387 -0
- sqlalchemy/orm/exc.py +237 -0
- sqlalchemy/orm/identity.py +302 -0
- sqlalchemy/orm/instrumentation.py +749 -0
- sqlalchemy/orm/interfaces.py +1595 -0
- sqlalchemy/orm/loading.py +1712 -0
- sqlalchemy/orm/mapped_collection.py +557 -0
- sqlalchemy/orm/mapper.py +4465 -0
- sqlalchemy/orm/path_registry.py +907 -0
- sqlalchemy/orm/persistence.py +1790 -0
- sqlalchemy/orm/properties.py +972 -0
- sqlalchemy/orm/query.py +3528 -0
- sqlalchemy/orm/relationships.py +3608 -0
- sqlalchemy/orm/scoping.py +2233 -0
- sqlalchemy/orm/session.py +5468 -0
- sqlalchemy/orm/state.py +1175 -0
- sqlalchemy/orm/state_changes.py +196 -0
- sqlalchemy/orm/strategies.py +3552 -0
- sqlalchemy/orm/strategy_options.py +2648 -0
- sqlalchemy/orm/sync.py +164 -0
- sqlalchemy/orm/unitofwork.py +797 -0
- sqlalchemy/orm/util.py +2461 -0
- sqlalchemy/orm/writeonly.py +701 -0
- sqlalchemy/pool/__init__.py +41 -0
- sqlalchemy/pool/base.py +1540 -0
- sqlalchemy/pool/events.py +375 -0
- sqlalchemy/pool/impl.py +583 -0
- sqlalchemy/py.typed +0 -0
- sqlalchemy/schema.py +75 -0
- sqlalchemy/sql/__init__.py +156 -0
- sqlalchemy/sql/_annotated_cols.py +402 -0
- sqlalchemy/sql/_cache_key_cy.cp315-win_amd64.pyd +0 -0
- sqlalchemy/sql/_cache_key_cy.py +363 -0
- sqlalchemy/sql/_dml_constructors.py +132 -0
- sqlalchemy/sql/_elements_constructors.py +2190 -0
- sqlalchemy/sql/_orm_types.py +19 -0
- sqlalchemy/sql/_selectable_constructors.py +840 -0
- sqlalchemy/sql/_typing.py +500 -0
- sqlalchemy/sql/_util_cy.cp315-win_amd64.pyd +0 -0
- sqlalchemy/sql/_util_cy.pxd +11 -0
- sqlalchemy/sql/_util_cy.py +127 -0
- sqlalchemy/sql/annotation.py +590 -0
- sqlalchemy/sql/base.py +2702 -0
- sqlalchemy/sql/cache_key.py +915 -0
- sqlalchemy/sql/coercions.py +1373 -0
- sqlalchemy/sql/compiler.py +8453 -0
- sqlalchemy/sql/crud.py +1816 -0
- sqlalchemy/sql/ddl.py +1962 -0
- sqlalchemy/sql/default_comparator.py +660 -0
- sqlalchemy/sql/dml.py +2018 -0
- sqlalchemy/sql/elements.py +6057 -0
- sqlalchemy/sql/events.py +458 -0
- sqlalchemy/sql/expression.py +171 -0
- sqlalchemy/sql/functions.py +2380 -0
- sqlalchemy/sql/lambdas.py +1442 -0
- sqlalchemy/sql/naming.py +204 -0
- sqlalchemy/sql/operators.py +2909 -0
- sqlalchemy/sql/roles.py +332 -0
- sqlalchemy/sql/schema.py +7075 -0
- sqlalchemy/sql/selectable.py +7634 -0
- sqlalchemy/sql/sqltypes.py +4130 -0
- sqlalchemy/sql/traversals.py +1041 -0
- sqlalchemy/sql/type_api.py +2450 -0
- sqlalchemy/sql/util.py +1496 -0
- sqlalchemy/sql/visitors.py +1153 -0
- sqlalchemy/testing/__init__.py +97 -0
- sqlalchemy/testing/assertions.py +1007 -0
- sqlalchemy/testing/assertsql.py +519 -0
- sqlalchemy/testing/asyncio.py +128 -0
- sqlalchemy/testing/cancellation.py +237 -0
- sqlalchemy/testing/config.py +440 -0
- sqlalchemy/testing/engines.py +482 -0
- sqlalchemy/testing/entities.py +117 -0
- sqlalchemy/testing/exclusions.py +501 -0
- sqlalchemy/testing/fixtures/__init__.py +30 -0
- sqlalchemy/testing/fixtures/base.py +426 -0
- sqlalchemy/testing/fixtures/mypy.py +247 -0
- sqlalchemy/testing/fixtures/orm.py +227 -0
- sqlalchemy/testing/fixtures/sql.py +538 -0
- sqlalchemy/testing/pickleable.py +155 -0
- sqlalchemy/testing/plugin/__init__.py +6 -0
- sqlalchemy/testing/plugin/bootstrap.py +50 -0
- sqlalchemy/testing/plugin/plugin_base.py +828 -0
- sqlalchemy/testing/plugin/pytestplugin.py +896 -0
- sqlalchemy/testing/profiles_file.py +350 -0
- sqlalchemy/testing/profiling.py +294 -0
- sqlalchemy/testing/provision.py +633 -0
- sqlalchemy/testing/requirements.py +1971 -0
- sqlalchemy/testing/schema.py +198 -0
- sqlalchemy/testing/suite/__init__.py +19 -0
- sqlalchemy/testing/suite/test_cte.py +237 -0
- sqlalchemy/testing/suite/test_ddl.py +420 -0
- sqlalchemy/testing/suite/test_dialect.py +776 -0
- sqlalchemy/testing/suite/test_insert.py +630 -0
- sqlalchemy/testing/suite/test_reflection.py +3815 -0
- sqlalchemy/testing/suite/test_results.py +660 -0
- sqlalchemy/testing/suite/test_rowcount.py +258 -0
- sqlalchemy/testing/suite/test_select.py +2112 -0
- sqlalchemy/testing/suite/test_sequence.py +317 -0
- sqlalchemy/testing/suite/test_table_via_select.py +686 -0
- sqlalchemy/testing/suite/test_types.py +2271 -0
- sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
- sqlalchemy/testing/suite/test_update_delete.py +139 -0
- sqlalchemy/testing/util.py +575 -0
- sqlalchemy/testing/warnings.py +52 -0
- sqlalchemy/types.py +75 -0
- sqlalchemy/util/__init__.py +165 -0
- sqlalchemy/util/_collections.py +688 -0
- sqlalchemy/util/_collections_cy.cp315-win_amd64.pyd +0 -0
- sqlalchemy/util/_collections_cy.pxd +8 -0
- sqlalchemy/util/_collections_cy.py +516 -0
- sqlalchemy/util/_has_cython.py +48 -0
- sqlalchemy/util/_immutabledict_cy.cp315-win_amd64.pyd +0 -0
- sqlalchemy/util/_immutabledict_cy.py +240 -0
- sqlalchemy/util/compat.py +298 -0
- sqlalchemy/util/concurrency.py +272 -0
- sqlalchemy/util/cython.py +95 -0
- sqlalchemy/util/deprecations.py +401 -0
- sqlalchemy/util/langhelpers.py +2797 -0
- sqlalchemy/util/preloaded.py +153 -0
- sqlalchemy/util/queue.py +304 -0
- sqlalchemy/util/tool_support.py +202 -0
- sqlalchemy/util/topological.py +120 -0
- sqlalchemy/util/typing.py +709 -0
- sqlalchemy-2.1.0rc1.dist-info/METADATA +270 -0
- sqlalchemy-2.1.0rc1.dist-info/RECORD +276 -0
- sqlalchemy-2.1.0rc1.dist-info/WHEEL +5 -0
- sqlalchemy-2.1.0rc1.dist-info/licenses/AUTHORS +30 -0
- sqlalchemy-2.1.0rc1.dist-info/licenses/LICENSE +19 -0
- sqlalchemy-2.1.0rc1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
# engine/_result_cy.py
|
|
2
|
+
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
|
3
|
+
# <see AUTHORS file>
|
|
4
|
+
#
|
|
5
|
+
# This module is part of SQLAlchemy and is released under
|
|
6
|
+
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
|
7
|
+
# mypy: disable-error-code="misc,no-redef,type-arg,untyped-decorator"
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from collections.abc import Iterator
|
|
12
|
+
from collections.abc import Sequence
|
|
13
|
+
from enum import Enum
|
|
14
|
+
import operator
|
|
15
|
+
from typing import Any
|
|
16
|
+
from typing import cast
|
|
17
|
+
from typing import Generic
|
|
18
|
+
from typing import Literal
|
|
19
|
+
from typing import overload
|
|
20
|
+
from typing import TYPE_CHECKING
|
|
21
|
+
from typing import TypeVar
|
|
22
|
+
from typing import Union
|
|
23
|
+
|
|
24
|
+
from .row import Row
|
|
25
|
+
from .row import RowMapping
|
|
26
|
+
from .. import exc
|
|
27
|
+
from ..util import HasMemoized_ro_memoized_attribute
|
|
28
|
+
from ..util.typing import Self
|
|
29
|
+
from ..util.typing import TupleAny
|
|
30
|
+
from ..util.typing import Unpack
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from .result import _ProcessorsType
|
|
34
|
+
from .result import Result
|
|
35
|
+
from .result import ResultMetaData
|
|
36
|
+
|
|
37
|
+
# START GENERATED CYTHON IMPORT
|
|
38
|
+
# This section is automatically generated by the script tools/cython_imports.py
|
|
39
|
+
try:
|
|
40
|
+
# NOTE: the cython compiler needs this "import cython" in the file, it
|
|
41
|
+
# can't be only "from sqlalchemy.util import cython" with the fallback
|
|
42
|
+
# in that module
|
|
43
|
+
import cython
|
|
44
|
+
except ModuleNotFoundError:
|
|
45
|
+
from sqlalchemy.util import cython
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _is_compiled() -> bool:
|
|
49
|
+
"""Utility function to indicate if this module is compiled or not."""
|
|
50
|
+
return cython.compiled # type: ignore[no-any-return,unused-ignore]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# END GENERATED CYTHON IMPORT
|
|
54
|
+
|
|
55
|
+
if cython.compiled:
|
|
56
|
+
from cython.cimports.cpython import Py_INCREF
|
|
57
|
+
from cython.cimports.cpython import PyList_New
|
|
58
|
+
from cython.cimports.cpython import PyList_SET_ITEM
|
|
59
|
+
from cython.cimports.cpython import PyTuple_New
|
|
60
|
+
from cython.cimports.cpython import PyTuple_SET_ITEM
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
_RowData = Row[Unpack[TupleAny]] | RowMapping | Any
|
|
64
|
+
"""A generic form of "row" that accommodates for the different kinds of
|
|
65
|
+
"rows" that different result objects return, including row, row mapping, and
|
|
66
|
+
scalar values"""
|
|
67
|
+
_R = TypeVar("_R", bound=_RowData)
|
|
68
|
+
_T = TypeVar("_T", bound=Any)
|
|
69
|
+
|
|
70
|
+
_InterimRowType = Union[_R, TupleAny]
|
|
71
|
+
"""a catchall "anything" kind of return type that can be applied
|
|
72
|
+
across all the result types
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
_UniqueFilterType = Callable[[Any], Any]
|
|
77
|
+
_UniqueFilterStateType = tuple[set[Any], _UniqueFilterType | None]
|
|
78
|
+
|
|
79
|
+
_FLAG_SIMPLE = cython.declare(cython.const[cython.char], 0)
|
|
80
|
+
_FLAG_SCALAR_TO_TUPLE = cython.declare(cython.const[cython.char], 1)
|
|
81
|
+
_FLAG_TUPLE_FILTER = cython.declare(cython.const[cython.char], 2)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# a symbol that indicates to internal Result methods that
|
|
85
|
+
# "no row is returned". We can't use None for those cases where a scalar
|
|
86
|
+
# filter is applied to rows.
|
|
87
|
+
class _NoRow(Enum):
|
|
88
|
+
_NO_ROW = 0
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
_NO_ROW = _NoRow._NO_ROW
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class BaseResultInternal(Generic[_R]):
|
|
95
|
+
__slots__ = ()
|
|
96
|
+
|
|
97
|
+
_real_result: Result[Unpack[TupleAny]] | None = None
|
|
98
|
+
_generate_rows: bool = True
|
|
99
|
+
_row_logging_fn: Callable[[Any], Any] | None
|
|
100
|
+
|
|
101
|
+
_unique_filter_state: _UniqueFilterStateType | None = None
|
|
102
|
+
_post_creational_filter: Callable[[Any], Any] | None = None
|
|
103
|
+
|
|
104
|
+
_metadata: ResultMetaData
|
|
105
|
+
|
|
106
|
+
_source_supports_scalars: bool
|
|
107
|
+
_yield_per: int | None
|
|
108
|
+
|
|
109
|
+
def _fetchiter_impl(
|
|
110
|
+
self,
|
|
111
|
+
) -> Iterator[_InterimRowType[Row[Unpack[TupleAny]]]]:
|
|
112
|
+
raise NotImplementedError()
|
|
113
|
+
|
|
114
|
+
def _fetchone_impl(
|
|
115
|
+
self, hard_close: bool = False
|
|
116
|
+
) -> _InterimRowType[Row[Unpack[TupleAny]]] | None:
|
|
117
|
+
raise NotImplementedError()
|
|
118
|
+
|
|
119
|
+
def _fetchmany_impl(
|
|
120
|
+
self, size: int | None = None
|
|
121
|
+
) -> list[_InterimRowType[Row[Unpack[TupleAny]]]]:
|
|
122
|
+
raise NotImplementedError()
|
|
123
|
+
|
|
124
|
+
def _fetchall_impl(
|
|
125
|
+
self,
|
|
126
|
+
) -> list[_InterimRowType[Row[Unpack[TupleAny]]]]:
|
|
127
|
+
raise NotImplementedError()
|
|
128
|
+
|
|
129
|
+
def _soft_close(self, hard: bool = False) -> None:
|
|
130
|
+
raise NotImplementedError()
|
|
131
|
+
|
|
132
|
+
@HasMemoized_ro_memoized_attribute
|
|
133
|
+
def _row_getter(
|
|
134
|
+
self,
|
|
135
|
+
) -> tuple[
|
|
136
|
+
Callable[..., _R] | None,
|
|
137
|
+
Callable[..., Sequence[_R]] | None,
|
|
138
|
+
Callable[..., Sequence[Any]] | None,
|
|
139
|
+
]:
|
|
140
|
+
"""Return a three-tuple of callables
|
|
141
|
+
``(single_row, many_rows, interim_rows)``.
|
|
142
|
+
|
|
143
|
+
``single_row`` and ``many_rows`` produce :class:`.Row` objects for
|
|
144
|
+
one input row or a sequence of them. ``interim_rows`` produces
|
|
145
|
+
rows in "interim" form for a sequence of input rows: plain tuples
|
|
146
|
+
with result processors and tuple filters applied but no
|
|
147
|
+
:class:`.Row` construction, falling back to ``many_rows`` when
|
|
148
|
+
Row objects are required (row logging, scalar sources).
|
|
149
|
+
|
|
150
|
+
"""
|
|
151
|
+
real_result = self if self._real_result is None else self._real_result
|
|
152
|
+
|
|
153
|
+
metadata = self._metadata
|
|
154
|
+
tuple_filters = metadata._tuplefilter
|
|
155
|
+
flag: cython.char = _FLAG_SIMPLE
|
|
156
|
+
|
|
157
|
+
if real_result._source_supports_scalars:
|
|
158
|
+
if not self._generate_rows:
|
|
159
|
+
return None, None, None
|
|
160
|
+
else:
|
|
161
|
+
flag = _FLAG_SCALAR_TO_TUPLE
|
|
162
|
+
elif tuple_filters is not None:
|
|
163
|
+
flag = _FLAG_TUPLE_FILTER
|
|
164
|
+
|
|
165
|
+
processors: tuple
|
|
166
|
+
proc_valid: tuple
|
|
167
|
+
|
|
168
|
+
if metadata._effective_processors is not None:
|
|
169
|
+
ep = metadata._effective_processors
|
|
170
|
+
if flag == _FLAG_TUPLE_FILTER:
|
|
171
|
+
ep = tuple_filters(ep)
|
|
172
|
+
|
|
173
|
+
processors = tuple(ep)
|
|
174
|
+
proc_valid = tuple(
|
|
175
|
+
[i for i, p in enumerate(processors) if p is not None]
|
|
176
|
+
)
|
|
177
|
+
else:
|
|
178
|
+
processors = ()
|
|
179
|
+
proc_valid = ()
|
|
180
|
+
|
|
181
|
+
proc_size: cython.Py_ssize_t = len(processors)
|
|
182
|
+
log_row = real_result._row_logging_fn
|
|
183
|
+
has_log_row: cython.bint = log_row is not None
|
|
184
|
+
|
|
185
|
+
key_to_index = metadata._key_to_index
|
|
186
|
+
_Row = Row
|
|
187
|
+
|
|
188
|
+
if flag == _FLAG_SIMPLE and proc_size == 0 and not has_log_row:
|
|
189
|
+
# just build the rows
|
|
190
|
+
|
|
191
|
+
def single_row_simple(input_row: Sequence[Any], /) -> Row:
|
|
192
|
+
return _Row(metadata, None, key_to_index, input_row)
|
|
193
|
+
|
|
194
|
+
if cython.compiled:
|
|
195
|
+
|
|
196
|
+
def many_rows_simple(rows: Sequence[Any], /) -> list[Any]:
|
|
197
|
+
size: cython.Py_hash_t = len(rows)
|
|
198
|
+
i: cython.Py_ssize_t
|
|
199
|
+
result: list = PyList_New(size)
|
|
200
|
+
for i in range(size):
|
|
201
|
+
row: object = _Row(
|
|
202
|
+
metadata, None, key_to_index, rows[i]
|
|
203
|
+
)
|
|
204
|
+
Py_INCREF(row)
|
|
205
|
+
PyList_SET_ITEM(result, i, row)
|
|
206
|
+
return result
|
|
207
|
+
|
|
208
|
+
else:
|
|
209
|
+
|
|
210
|
+
def many_rows_simple(rows: Sequence[Any], /) -> list[Any]:
|
|
211
|
+
return [
|
|
212
|
+
_Row(metadata, None, key_to_index, row) for row in rows
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
def interim_rows_simple(rows: Sequence[Any], /) -> Sequence[Any]:
|
|
216
|
+
# interim rows must be tuples so that position-based
|
|
217
|
+
# getters and slices apply; most DBAPIs return tuples
|
|
218
|
+
# already
|
|
219
|
+
if rows and type(rows[0]) is not tuple:
|
|
220
|
+
return [tuple(row) for row in rows]
|
|
221
|
+
return rows
|
|
222
|
+
|
|
223
|
+
return single_row_simple, many_rows_simple, interim_rows_simple # type: ignore[return-value] # noqa: E501
|
|
224
|
+
|
|
225
|
+
first_row: cython.bint = True
|
|
226
|
+
|
|
227
|
+
def single_row(input_row: Sequence[Any], /) -> Row:
|
|
228
|
+
nonlocal first_row
|
|
229
|
+
|
|
230
|
+
if flag == _FLAG_SCALAR_TO_TUPLE:
|
|
231
|
+
input_row = (input_row,)
|
|
232
|
+
elif flag == _FLAG_TUPLE_FILTER:
|
|
233
|
+
input_row = tuple_filters(input_row)
|
|
234
|
+
|
|
235
|
+
if proc_size != 0:
|
|
236
|
+
if first_row:
|
|
237
|
+
first_row = False
|
|
238
|
+
assert len(input_row) == proc_size
|
|
239
|
+
input_row = _apply_processors(
|
|
240
|
+
processors, proc_size, proc_valid, input_row
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
row: Row = _Row(metadata, None, key_to_index, input_row)
|
|
244
|
+
if has_log_row:
|
|
245
|
+
row = log_row(row)
|
|
246
|
+
return row
|
|
247
|
+
|
|
248
|
+
if cython.compiled:
|
|
249
|
+
|
|
250
|
+
def many_rows(rows: Sequence[Any], /) -> list[Any]:
|
|
251
|
+
size: cython.Py_hash_t = len(rows)
|
|
252
|
+
i: cython.Py_ssize_t
|
|
253
|
+
result: list = PyList_New(size)
|
|
254
|
+
for i in range(size):
|
|
255
|
+
row: object = single_row(rows[i])
|
|
256
|
+
Py_INCREF(row)
|
|
257
|
+
PyList_SET_ITEM(result, i, row)
|
|
258
|
+
return result
|
|
259
|
+
|
|
260
|
+
else:
|
|
261
|
+
|
|
262
|
+
def many_rows(rows: Sequence[Any], /) -> list[Any]:
|
|
263
|
+
return [single_row(row) for row in rows]
|
|
264
|
+
|
|
265
|
+
if flag == _FLAG_SCALAR_TO_TUPLE or has_log_row:
|
|
266
|
+
# scalar-source rows have no tuple form; row logging requires
|
|
267
|
+
# the Row objects themselves
|
|
268
|
+
interim_rows = many_rows
|
|
269
|
+
else:
|
|
270
|
+
|
|
271
|
+
def single_interim_row(input_row: Sequence[Any], /) -> Any:
|
|
272
|
+
if flag == _FLAG_TUPLE_FILTER:
|
|
273
|
+
input_row = tuple_filters(input_row)
|
|
274
|
+
if proc_size != 0:
|
|
275
|
+
input_row = _apply_processors(
|
|
276
|
+
processors, proc_size, proc_valid, input_row
|
|
277
|
+
)
|
|
278
|
+
elif type(input_row) is not tuple:
|
|
279
|
+
input_row = tuple(input_row)
|
|
280
|
+
return input_row
|
|
281
|
+
|
|
282
|
+
if cython.compiled:
|
|
283
|
+
|
|
284
|
+
def interim_rows(rows: Sequence[Any], /) -> Sequence[Any]:
|
|
285
|
+
size: cython.Py_hash_t = len(rows)
|
|
286
|
+
i: cython.Py_ssize_t
|
|
287
|
+
result: list = PyList_New(size)
|
|
288
|
+
for i in range(size):
|
|
289
|
+
row: object = single_interim_row(rows[i])
|
|
290
|
+
Py_INCREF(row)
|
|
291
|
+
PyList_SET_ITEM(result, i, row)
|
|
292
|
+
return result
|
|
293
|
+
|
|
294
|
+
else:
|
|
295
|
+
|
|
296
|
+
def interim_rows(rows: Sequence[Any], /) -> Sequence[Any]:
|
|
297
|
+
return [single_interim_row(row) for row in rows]
|
|
298
|
+
|
|
299
|
+
return single_row, many_rows, interim_rows # type: ignore[return-value] # noqa: E501
|
|
300
|
+
|
|
301
|
+
@HasMemoized_ro_memoized_attribute
|
|
302
|
+
def _iterator_getter(self) -> Callable[[], Iterator[_R]]:
|
|
303
|
+
make_row = self._row_getter[0]
|
|
304
|
+
|
|
305
|
+
post_creational_filter = self._post_creational_filter
|
|
306
|
+
|
|
307
|
+
if self._unique_filter_state is not None:
|
|
308
|
+
uniques: set
|
|
309
|
+
uniques, strategy = self._unique_strategy
|
|
310
|
+
|
|
311
|
+
def iterrows() -> Iterator[_R]:
|
|
312
|
+
for raw_row in self._fetchiter_impl():
|
|
313
|
+
row = (
|
|
314
|
+
make_row(raw_row) if make_row is not None else raw_row
|
|
315
|
+
)
|
|
316
|
+
hashed = strategy(row) if strategy is not None else row
|
|
317
|
+
if hashed in uniques:
|
|
318
|
+
continue
|
|
319
|
+
uniques.add(hashed)
|
|
320
|
+
if post_creational_filter is not None:
|
|
321
|
+
row = post_creational_filter(row)
|
|
322
|
+
yield row
|
|
323
|
+
|
|
324
|
+
else:
|
|
325
|
+
|
|
326
|
+
def iterrows() -> Iterator[_R]:
|
|
327
|
+
for raw_row in self._fetchiter_impl():
|
|
328
|
+
row = (
|
|
329
|
+
make_row(raw_row) if make_row is not None else raw_row
|
|
330
|
+
)
|
|
331
|
+
if post_creational_filter is not None:
|
|
332
|
+
row = post_creational_filter(row)
|
|
333
|
+
yield row
|
|
334
|
+
|
|
335
|
+
return iterrows
|
|
336
|
+
|
|
337
|
+
def _raw_all_tuples(self) -> Sequence[tuple[Any, ...]]:
|
|
338
|
+
"""Return all remaining rows as plain processed tuples, as produced
|
|
339
|
+
by the ``interim_rows`` element of :attr:`._row_getter`;
|
|
340
|
+
:class:`.Row` objects are produced instead where required (row
|
|
341
|
+
logging, scalar sources).
|
|
342
|
+
|
|
343
|
+
Used by ORM loading, whose row getters are position-based and
|
|
344
|
+
accept any tuple-like row.
|
|
345
|
+
|
|
346
|
+
Uniquing and post-creational filters are not applied here, so
|
|
347
|
+
may not be present on the :class:`.Result` when this method is
|
|
348
|
+
used.
|
|
349
|
+
|
|
350
|
+
"""
|
|
351
|
+
assert self._unique_filter_state is None
|
|
352
|
+
assert self._post_creational_filter is None
|
|
353
|
+
interim_rows = self._row_getter[2]
|
|
354
|
+
assert interim_rows is not None
|
|
355
|
+
rows = self._fetchall_impl()
|
|
356
|
+
return interim_rows(rows)
|
|
357
|
+
|
|
358
|
+
def _allrows(self) -> Sequence[_R]:
|
|
359
|
+
post_creational_filter = self._post_creational_filter
|
|
360
|
+
|
|
361
|
+
make_rows = self._row_getter[1]
|
|
362
|
+
|
|
363
|
+
rows = self._fetchall_impl()
|
|
364
|
+
made_rows: Sequence[_InterimRowType[_R]]
|
|
365
|
+
if make_rows is not None:
|
|
366
|
+
made_rows = make_rows(rows)
|
|
367
|
+
else:
|
|
368
|
+
made_rows = rows
|
|
369
|
+
|
|
370
|
+
interim_rows: Sequence[_R]
|
|
371
|
+
|
|
372
|
+
if self._unique_filter_state is not None:
|
|
373
|
+
uniques: set
|
|
374
|
+
uniques, strategy = self._unique_strategy
|
|
375
|
+
interim_rows = _apply_unique_strategy(
|
|
376
|
+
made_rows, [], uniques, strategy
|
|
377
|
+
)
|
|
378
|
+
else:
|
|
379
|
+
interim_rows = made_rows # type: ignore[assignment]
|
|
380
|
+
|
|
381
|
+
if post_creational_filter is not None:
|
|
382
|
+
interim_rows = [
|
|
383
|
+
post_creational_filter(row) for row in interim_rows
|
|
384
|
+
]
|
|
385
|
+
return interim_rows
|
|
386
|
+
|
|
387
|
+
@HasMemoized_ro_memoized_attribute
|
|
388
|
+
def _onerow_getter(
|
|
389
|
+
self,
|
|
390
|
+
) -> Callable[[Self], Literal[_NoRow._NO_ROW] | _R]:
|
|
391
|
+
make_row = self._row_getter[0]
|
|
392
|
+
|
|
393
|
+
post_creational_filter = self._post_creational_filter
|
|
394
|
+
|
|
395
|
+
if self._unique_filter_state is not None:
|
|
396
|
+
uniques: set
|
|
397
|
+
uniques, strategy = self._unique_strategy
|
|
398
|
+
|
|
399
|
+
def onerow(self: Self) -> Literal[_NoRow._NO_ROW] | _R:
|
|
400
|
+
while True:
|
|
401
|
+
row = self._fetchone_impl()
|
|
402
|
+
if row is None:
|
|
403
|
+
return _NO_ROW
|
|
404
|
+
else:
|
|
405
|
+
obj: _InterimRowType[Any] = (
|
|
406
|
+
make_row(row) if make_row is not None else row
|
|
407
|
+
)
|
|
408
|
+
hashed = strategy(obj) if strategy is not None else obj
|
|
409
|
+
if hashed in uniques:
|
|
410
|
+
continue
|
|
411
|
+
uniques.add(hashed)
|
|
412
|
+
if post_creational_filter is not None:
|
|
413
|
+
obj = post_creational_filter(obj)
|
|
414
|
+
return obj # type: ignore[return-value]
|
|
415
|
+
|
|
416
|
+
else:
|
|
417
|
+
|
|
418
|
+
def onerow(self: Self) -> Literal[_NoRow._NO_ROW] | _R:
|
|
419
|
+
row = self._fetchone_impl()
|
|
420
|
+
if row is None:
|
|
421
|
+
return _NO_ROW
|
|
422
|
+
else:
|
|
423
|
+
interim_row: _InterimRowType[Any] = (
|
|
424
|
+
make_row(row) if make_row is not None else row
|
|
425
|
+
)
|
|
426
|
+
if post_creational_filter is not None:
|
|
427
|
+
interim_row = post_creational_filter(interim_row)
|
|
428
|
+
return interim_row # type: ignore[return-value]
|
|
429
|
+
|
|
430
|
+
return onerow
|
|
431
|
+
|
|
432
|
+
@HasMemoized_ro_memoized_attribute
|
|
433
|
+
def _manyrow_getter(self) -> Callable[[Self, int | None], Sequence[_R]]:
|
|
434
|
+
make_rows = self._row_getter[1]
|
|
435
|
+
real_result = self if self._real_result is None else self._real_result
|
|
436
|
+
yield_per = real_result._yield_per
|
|
437
|
+
|
|
438
|
+
post_creational_filter = self._post_creational_filter
|
|
439
|
+
|
|
440
|
+
if self._unique_filter_state:
|
|
441
|
+
uniques: set
|
|
442
|
+
uniques, strategy = self._unique_strategy
|
|
443
|
+
|
|
444
|
+
def manyrows(self: Self, num: int | None, /) -> Sequence[_R]:
|
|
445
|
+
made_rows: Sequence[Any]
|
|
446
|
+
collect: list[_R] = []
|
|
447
|
+
|
|
448
|
+
_manyrows = self._fetchmany_impl
|
|
449
|
+
|
|
450
|
+
if num is None:
|
|
451
|
+
# if None is passed, we don't know the default
|
|
452
|
+
# manyrows number, DBAPI has this as cursor.arraysize
|
|
453
|
+
# different DBAPIs / fetch strategies may be different.
|
|
454
|
+
# do a fetch to find what the number is. if there are
|
|
455
|
+
# only fewer rows left, then it doesn't matter.
|
|
456
|
+
if yield_per:
|
|
457
|
+
num_required = num = yield_per
|
|
458
|
+
else:
|
|
459
|
+
rows = _manyrows()
|
|
460
|
+
num = len(rows)
|
|
461
|
+
made_rows = (
|
|
462
|
+
rows if make_rows is None else make_rows(rows)
|
|
463
|
+
)
|
|
464
|
+
_apply_unique_strategy(
|
|
465
|
+
made_rows, collect, uniques, strategy
|
|
466
|
+
)
|
|
467
|
+
num_required = num - len(collect)
|
|
468
|
+
else:
|
|
469
|
+
num_required = num
|
|
470
|
+
|
|
471
|
+
assert num is not None
|
|
472
|
+
|
|
473
|
+
while num_required:
|
|
474
|
+
rows = _manyrows(num_required)
|
|
475
|
+
if not rows:
|
|
476
|
+
break
|
|
477
|
+
|
|
478
|
+
made_rows = rows if make_rows is None else make_rows(rows)
|
|
479
|
+
_apply_unique_strategy(
|
|
480
|
+
made_rows, collect, uniques, strategy
|
|
481
|
+
)
|
|
482
|
+
num_required = num - len(collect)
|
|
483
|
+
|
|
484
|
+
if post_creational_filter is not None:
|
|
485
|
+
collect = [post_creational_filter(row) for row in collect]
|
|
486
|
+
return collect
|
|
487
|
+
|
|
488
|
+
else:
|
|
489
|
+
|
|
490
|
+
def manyrows(self: Self, num: int | None, /) -> Sequence[_R]:
|
|
491
|
+
if num is None:
|
|
492
|
+
num = yield_per
|
|
493
|
+
|
|
494
|
+
rows: Sequence = self._fetchmany_impl(num)
|
|
495
|
+
if make_rows is not None:
|
|
496
|
+
rows = make_rows(rows)
|
|
497
|
+
if post_creational_filter is not None:
|
|
498
|
+
rows = [post_creational_filter(row) for row in rows]
|
|
499
|
+
return rows
|
|
500
|
+
|
|
501
|
+
return manyrows
|
|
502
|
+
|
|
503
|
+
@overload
|
|
504
|
+
def _only_one_row(
|
|
505
|
+
self: BaseResultInternal[Row[_T, Unpack[TupleAny]]],
|
|
506
|
+
raise_for_second_row: bool,
|
|
507
|
+
raise_for_none: bool,
|
|
508
|
+
scalar: Literal[True],
|
|
509
|
+
) -> _T: ...
|
|
510
|
+
|
|
511
|
+
@overload
|
|
512
|
+
def _only_one_row(
|
|
513
|
+
self,
|
|
514
|
+
raise_for_second_row: bool,
|
|
515
|
+
raise_for_none: Literal[True],
|
|
516
|
+
scalar: bool,
|
|
517
|
+
) -> _R: ...
|
|
518
|
+
|
|
519
|
+
@overload
|
|
520
|
+
def _only_one_row(
|
|
521
|
+
self,
|
|
522
|
+
raise_for_second_row: bool,
|
|
523
|
+
raise_for_none: bool,
|
|
524
|
+
scalar: bool,
|
|
525
|
+
) -> _R | None: ...
|
|
526
|
+
|
|
527
|
+
def _only_one_row(
|
|
528
|
+
self,
|
|
529
|
+
raise_for_second_row: bool,
|
|
530
|
+
raise_for_none: bool,
|
|
531
|
+
scalar: bool,
|
|
532
|
+
) -> _R | None:
|
|
533
|
+
onerow = self._fetchone_impl
|
|
534
|
+
|
|
535
|
+
row = onerow(hard_close=True)
|
|
536
|
+
if row is None:
|
|
537
|
+
if raise_for_none:
|
|
538
|
+
raise exc.NoResultFound(
|
|
539
|
+
"No row was found when one was required"
|
|
540
|
+
)
|
|
541
|
+
else:
|
|
542
|
+
return None
|
|
543
|
+
|
|
544
|
+
if scalar and self._source_supports_scalars:
|
|
545
|
+
self._generate_rows = False
|
|
546
|
+
make_row = None
|
|
547
|
+
else:
|
|
548
|
+
make_row = self._row_getter[0]
|
|
549
|
+
|
|
550
|
+
try:
|
|
551
|
+
row = make_row(row) if make_row is not None else row # type: ignore[assignment] # noqa: E501
|
|
552
|
+
except:
|
|
553
|
+
self._soft_close(hard=True)
|
|
554
|
+
raise
|
|
555
|
+
|
|
556
|
+
if raise_for_second_row:
|
|
557
|
+
if self._unique_filter_state:
|
|
558
|
+
# for no second row but uniqueness, need to essentially
|
|
559
|
+
# consume the entire result :(
|
|
560
|
+
strategy = self._unique_strategy[1]
|
|
561
|
+
|
|
562
|
+
existing_row_hash = (
|
|
563
|
+
strategy(row) if strategy is not None else row
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
while True:
|
|
567
|
+
next_row: Any = onerow(hard_close=True)
|
|
568
|
+
if next_row is None:
|
|
569
|
+
next_row = _NO_ROW
|
|
570
|
+
break
|
|
571
|
+
|
|
572
|
+
try:
|
|
573
|
+
next_row = (
|
|
574
|
+
make_row(next_row)
|
|
575
|
+
if make_row is not None
|
|
576
|
+
else next_row
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
if strategy is not None:
|
|
580
|
+
# assert next_row is not _NO_ROW
|
|
581
|
+
if existing_row_hash == strategy(next_row):
|
|
582
|
+
continue
|
|
583
|
+
elif row == next_row:
|
|
584
|
+
continue
|
|
585
|
+
# here, we have a row and it's different
|
|
586
|
+
break
|
|
587
|
+
except:
|
|
588
|
+
self._soft_close(hard=True)
|
|
589
|
+
raise
|
|
590
|
+
else:
|
|
591
|
+
next_row = onerow(hard_close=True)
|
|
592
|
+
if next_row is None:
|
|
593
|
+
next_row = _NO_ROW
|
|
594
|
+
|
|
595
|
+
if next_row is not _NO_ROW:
|
|
596
|
+
self._soft_close(hard=True)
|
|
597
|
+
raise exc.MultipleResultsFound(
|
|
598
|
+
"Multiple rows were found when exactly one was required"
|
|
599
|
+
if raise_for_none
|
|
600
|
+
else "Multiple rows were found when one or none "
|
|
601
|
+
"was required"
|
|
602
|
+
)
|
|
603
|
+
else:
|
|
604
|
+
next_row = _NO_ROW
|
|
605
|
+
# if we checked for second row then that would have
|
|
606
|
+
# closed us :)
|
|
607
|
+
self._soft_close(hard=True)
|
|
608
|
+
|
|
609
|
+
if not scalar:
|
|
610
|
+
post_creational_filter = self._post_creational_filter
|
|
611
|
+
if post_creational_filter is not None:
|
|
612
|
+
row = post_creational_filter(row)
|
|
613
|
+
|
|
614
|
+
if scalar and make_row is not None:
|
|
615
|
+
return row[0] # type: ignore[no-any-return]
|
|
616
|
+
else:
|
|
617
|
+
return row # type: ignore[return-value]
|
|
618
|
+
|
|
619
|
+
def _iter_impl(self) -> Iterator[_R]:
|
|
620
|
+
return self._iterator_getter()
|
|
621
|
+
|
|
622
|
+
def _next_impl(self) -> _R:
|
|
623
|
+
row = self._onerow_getter(self)
|
|
624
|
+
if row is _NO_ROW:
|
|
625
|
+
raise StopIteration()
|
|
626
|
+
else:
|
|
627
|
+
return row
|
|
628
|
+
|
|
629
|
+
@HasMemoized_ro_memoized_attribute
|
|
630
|
+
def _unique_strategy(self) -> _UniqueFilterStateType:
|
|
631
|
+
assert self._unique_filter_state is not None
|
|
632
|
+
uniques, strategy = self._unique_filter_state
|
|
633
|
+
|
|
634
|
+
if (
|
|
635
|
+
strategy is None
|
|
636
|
+
and self._metadata._create_unique_filters is not None
|
|
637
|
+
):
|
|
638
|
+
real_result = cast(
|
|
639
|
+
"Result[Any]",
|
|
640
|
+
self if self._real_result is None else self._real_result,
|
|
641
|
+
)
|
|
642
|
+
filters = self._metadata._create_unique_filters(real_result)
|
|
643
|
+
if (
|
|
644
|
+
real_result._source_supports_scalars
|
|
645
|
+
and not self._generate_rows
|
|
646
|
+
):
|
|
647
|
+
strategy = filters[0]
|
|
648
|
+
else:
|
|
649
|
+
if self._metadata._tuplefilter is not None:
|
|
650
|
+
filters = self._metadata._tuplefilter(filters)
|
|
651
|
+
|
|
652
|
+
strategy = operator.methodcaller("_filter_on_values", filters)
|
|
653
|
+
return uniques, strategy
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
if cython.compiled:
|
|
657
|
+
|
|
658
|
+
@cython.inline
|
|
659
|
+
@cython.cfunc
|
|
660
|
+
@cython.wraparound(False)
|
|
661
|
+
@cython.boundscheck(False)
|
|
662
|
+
def _apply_processors(
|
|
663
|
+
proc: tuple,
|
|
664
|
+
proc_size: cython.Py_ssize_t,
|
|
665
|
+
proc_valid: object, # used only by python impl
|
|
666
|
+
data: Sequence,
|
|
667
|
+
) -> tuple[Any, ...]:
|
|
668
|
+
res: tuple = PyTuple_New(proc_size)
|
|
669
|
+
i: cython.Py_ssize_t
|
|
670
|
+
for i in range(proc_size):
|
|
671
|
+
p = proc[i]
|
|
672
|
+
if p is not None:
|
|
673
|
+
value = p(data[i])
|
|
674
|
+
else:
|
|
675
|
+
value = data[i]
|
|
676
|
+
Py_INCREF(value)
|
|
677
|
+
PyTuple_SET_ITEM(res, i, value)
|
|
678
|
+
return res
|
|
679
|
+
|
|
680
|
+
else:
|
|
681
|
+
|
|
682
|
+
def _apply_processors(
|
|
683
|
+
proc: _ProcessorsType,
|
|
684
|
+
proc_size: int, # used only by cython impl
|
|
685
|
+
proc_valid: tuple[int, ...],
|
|
686
|
+
data: Sequence[Any],
|
|
687
|
+
) -> tuple[Any, ...]:
|
|
688
|
+
res = list(data)
|
|
689
|
+
for i in proc_valid:
|
|
690
|
+
res[i] = proc[i](res[i])
|
|
691
|
+
return tuple(res)
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
@cython.inline
|
|
695
|
+
@cython.cfunc
|
|
696
|
+
def _apply_unique_strategy(
|
|
697
|
+
rows: Sequence[Any],
|
|
698
|
+
destination: list[Any],
|
|
699
|
+
uniques: set[Any],
|
|
700
|
+
strategy: Callable[[Any], Any] | None,
|
|
701
|
+
) -> list[Any]:
|
|
702
|
+
i: cython.Py_ssize_t
|
|
703
|
+
has_strategy: cython.bint = strategy is not None
|
|
704
|
+
for i in range(len(rows)):
|
|
705
|
+
row = rows[i]
|
|
706
|
+
hashed = strategy(row) if has_strategy else row
|
|
707
|
+
if hashed in uniques:
|
|
708
|
+
continue
|
|
709
|
+
uniques.add(hashed)
|
|
710
|
+
destination.append(row)
|
|
711
|
+
return destination
|
|
Binary file
|