arrowsql 0.1.0__cp313-cp313-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.
Files changed (45) hide show
  1. arrowsql/__init__.py +653 -0
  2. arrowsql/_rust_auth.py +170 -0
  3. arrowsql/_server_sqlstate.py +11 -0
  4. arrowsql/_utf8_strings.py +78 -0
  5. arrowsql/agent.py +620 -0
  6. arrowsql/aio.py +533 -0
  7. arrowsql/arrow_parallel.py +464 -0
  8. arrowsql/async_query/__init__.py +40 -0
  9. arrowsql/async_query/_connection_context.py +39 -0
  10. arrowsql/async_query/_native.py +24 -0
  11. arrowsql/async_query/async_connection.py +160 -0
  12. arrowsql/async_query/async_cursor.py +225 -0
  13. arrowsql/async_query/async_execute.py +113 -0
  14. arrowsql/async_query/async_fetch.py +153 -0
  15. arrowsql/async_query/exception_translator.py +132 -0
  16. arrowsql/auth.py +913 -0
  17. arrowsql/bulk_parallel.py +385 -0
  18. arrowsql/connection.py +2377 -0
  19. arrowsql/connection_string_builder.py +114 -0
  20. arrowsql/connection_string_parser.py +417 -0
  21. arrowsql/constants.py +701 -0
  22. arrowsql/cursor.py +4516 -0
  23. arrowsql/db_connection.py +98 -0
  24. arrowsql/ddbc_bindings.cp313-win_amd64.pyd +0 -0
  25. arrowsql/ddbc_bindings.pyi +283 -0
  26. arrowsql/decimal_config.py +98 -0
  27. arrowsql/exceptions.py +378 -0
  28. arrowsql/helpers.py +436 -0
  29. arrowsql/logging.py +700 -0
  30. arrowsql/mssql_python.pyi +579 -0
  31. arrowsql/odbc_provider.py +269 -0
  32. arrowsql/parameter_helper.py +442 -0
  33. arrowsql/perf_timer.py +286 -0
  34. arrowsql/pooling.py +147 -0
  35. arrowsql/py.typed +1 -0
  36. arrowsql/row.py +462 -0
  37. arrowsql/type.py +160 -0
  38. arrowsql-0.1.0.dist-info/METADATA +146 -0
  39. arrowsql-0.1.0.dist-info/RECORD +45 -0
  40. arrowsql-0.1.0.dist-info/WHEEL +4 -0
  41. arrowsql-0.1.0.dist-info/entry_points.txt +2 -0
  42. arrowsql-0.1.0.dist-info/licenses/LICENSE +21 -0
  43. arrowsql-0.1.0.dist-info/licenses/LICENSE.upstream-mssql-python +47 -0
  44. arrowsql-0.1.0.dist-info/sboms/ddbc_bindings.cyclonedx.json +5195 -0
  45. mssql_python/__init__.py +39 -0
@@ -0,0 +1,160 @@
1
+ """Public asynchronous connection backed directly by mssql-py-core.
2
+
3
+ Warning:
4
+ Async query execution APIs are under active development and are not intended
5
+ for production use. Their signatures, behavior, error handling, and compatibility
6
+ may change without notice.
7
+ """
8
+
9
+ from typing import Any, Optional
10
+
11
+ from ..logging import logger
12
+ from ._native import load_py_core
13
+ from ._connection_context import build_async_connection_context
14
+ from .async_cursor import _AsyncCursor # pyright: ignore[reportPrivateUsage]
15
+ from .exception_translator import (
16
+ DataError,
17
+ DatabaseError,
18
+ Error,
19
+ IntegrityError,
20
+ InterfaceError,
21
+ InternalError,
22
+ NotSupportedError,
23
+ OperationalError,
24
+ ProgrammingError,
25
+ Warning,
26
+ translate_py_core_exceptions,
27
+ )
28
+
29
+
30
+ class _AsyncConnection:
31
+ """Thin Python wrapper over ``mssql_py_core.PyAsyncConnection``.
32
+
33
+ Warning:
34
+ This preview API is under active development and is not intended for production use.
35
+ Its signatures, behavior, error handling, and compatibility may change without notice.
36
+ """
37
+
38
+ Warning = Warning
39
+ Error = Error
40
+ InterfaceError = InterfaceError
41
+ DatabaseError = DatabaseError
42
+ DataError = DataError
43
+ OperationalError = OperationalError
44
+ IntegrityError = IntegrityError
45
+ InternalError = InternalError
46
+ ProgrammingError = ProgrammingError
47
+ NotSupportedError = NotSupportedError
48
+
49
+ def __init__(self, py_core_async_connection: Any) -> None:
50
+ self._py_core_async_connection = py_core_async_connection
51
+
52
+ @classmethod
53
+ async def connect(
54
+ cls,
55
+ connection_str: str = "",
56
+ autocommit: bool = False,
57
+ timeout: int = 0,
58
+ python_logger: Optional[Any] = None,
59
+ ) -> "_AsyncConnection":
60
+ """Establish an asynchronous connection from an ODBC connection string."""
61
+ logger_bridge = python_logger
62
+ if logger_bridge is None and logger.is_debug_enabled:
63
+ logger_bridge = logger
64
+ logger.debug(
65
+ "AsyncConnection.connect: starting; autocommit=%s; logger_source=%s",
66
+ autocommit,
67
+ (
68
+ "custom"
69
+ if python_logger is not None
70
+ else "mssql_python" if logger_bridge is not None else "disabled"
71
+ ),
72
+ )
73
+ with translate_py_core_exceptions():
74
+ client_context_dict = build_async_connection_context(connection_str, timeout)
75
+ py_core = load_py_core()
76
+ py_core_async_connection = await py_core.PyAsyncConnection.connect(
77
+ client_context_dict,
78
+ python_logger=logger_bridge,
79
+ autocommit=autocommit,
80
+ )
81
+ logger.debug("AsyncConnection.connect: connected")
82
+ return cls(py_core_async_connection)
83
+
84
+ def cursor(self) -> _AsyncCursor:
85
+ """Create a public asynchronous cursor sharing this connection."""
86
+ with translate_py_core_exceptions():
87
+ py_core_async_cursor = self._py_core_async_connection.cursor()
88
+ logger.debug("AsyncConnection.cursor: cursor created")
89
+ return _AsyncCursor(py_core_async_cursor, self)
90
+
91
+ async def commit(self) -> None:
92
+ """Commit the active transaction, if any."""
93
+ logger.debug("AsyncConnection.commit: starting")
94
+ with translate_py_core_exceptions():
95
+ await self._py_core_async_connection.commit()
96
+ logger.debug("AsyncConnection.commit: completed")
97
+
98
+ async def rollback(self) -> None:
99
+ """Roll back the active transaction, if any."""
100
+ logger.debug("AsyncConnection.rollback: starting")
101
+ with translate_py_core_exceptions():
102
+ await self._py_core_async_connection.rollback()
103
+ logger.debug("AsyncConnection.rollback: completed")
104
+
105
+ async def close(self) -> None:
106
+ """Close the py-core async connection."""
107
+ logger.debug("AsyncConnection.close: starting")
108
+ with translate_py_core_exceptions():
109
+ await self._py_core_async_connection.close()
110
+ logger.debug("AsyncConnection.close: completed")
111
+
112
+ async def __aenter__(self) -> "_AsyncConnection":
113
+ logger.debug("AsyncConnection.__aenter__: entering context")
114
+ with translate_py_core_exceptions():
115
+ await self._py_core_async_connection.__aenter__()
116
+ logger.debug("AsyncConnection.__aenter__: context entered")
117
+ return self
118
+
119
+ async def __aexit__(self, exc_type, exc_value, traceback) -> Any:
120
+ logger.debug(
121
+ "AsyncConnection.__aexit__: exiting context; block_error=%s",
122
+ exc_type is not None,
123
+ )
124
+ with translate_py_core_exceptions():
125
+ result = await self._py_core_async_connection.__aexit__(exc_type, exc_value, traceback)
126
+ logger.debug("AsyncConnection.__aexit__: context exited")
127
+ return result
128
+
129
+ @property
130
+ def timeout(self) -> int:
131
+ """Default query timeout inherited by subsequently created cursors."""
132
+ with translate_py_core_exceptions():
133
+ return self._py_core_async_connection.timeout
134
+
135
+ @timeout.setter
136
+ def timeout(self, value: int) -> None:
137
+ with translate_py_core_exceptions():
138
+ self._py_core_async_connection.timeout = value
139
+ logger.debug("AsyncConnection.timeout: updated")
140
+
141
+ @property
142
+ def autocommit(self) -> bool:
143
+ """Whether the connection was opened in autocommit mode."""
144
+ with translate_py_core_exceptions():
145
+ return self._py_core_async_connection.autocommit
146
+
147
+ @property
148
+ def closed(self) -> bool:
149
+ """Whether close has been initiated on the py-core async connection."""
150
+ with translate_py_core_exceptions():
151
+ return self._py_core_async_connection.closed
152
+
153
+ def is_connected(self) -> bool:
154
+ """Return whether the py-core async connection remains open."""
155
+ with translate_py_core_exceptions():
156
+ return self._py_core_async_connection.is_connected()
157
+
158
+ def __repr__(self) -> str:
159
+ state = "closed" if self.closed else "connected"
160
+ return f"_AsyncConnection({state})"
@@ -0,0 +1,225 @@
1
+ """Internal asynchronous cursor backed directly by mssql-py-core.
2
+
3
+ Warning:
4
+ Async query execution APIs are under active development and are not intended
5
+ for production use. Their signatures, behavior, error handling, and compatibility
6
+ may change without notice.
7
+ """
8
+
9
+ import asyncio
10
+ from collections.abc import Mapping, Sequence
11
+ from contextlib import asynccontextmanager
12
+ from typing import Any, Optional
13
+ import uuid
14
+
15
+ from ..helpers import get_settings
16
+ from ..logging import logger
17
+ from ..row import Row
18
+ from . import async_execute, async_fetch
19
+ from .exception_translator import is_connection_busy, translate_py_core_exceptions
20
+
21
+
22
+ class _AsyncCursor:
23
+ """Internal Python wrapper over ``mssql_py_core.PyAsyncCursor``.
24
+
25
+ Warning:
26
+ This preview API is under active development and is not intended for production use.
27
+ Its signatures, behavior, error handling, and compatibility may change without notice.
28
+ """
29
+
30
+ def __init__(self, py_core_async_cursor: Any, connection: Any = None) -> None:
31
+ self._py_core_async_cursor = py_core_async_cursor
32
+ self._connection = connection
33
+ self._closed = False
34
+ self._result_transition_lock = asyncio.Lock()
35
+ self._result_ready = asyncio.Event()
36
+ self._result_ready.set()
37
+ self._result_generation = 0
38
+ self._fetched_row_count = 0
39
+ self._fetch_rowcount: int | None = None
40
+ self._description: list[tuple[Any, ...]] | None = None
41
+ self._column_map: dict[str, int] = {}
42
+ self._column_map_lower: dict[str, int] | None = None
43
+ self._column_names: tuple[str, ...] | None = None
44
+ self._uuid_str_indices: tuple[int, ...] | None = None
45
+
46
+ def _clear_result_metadata(self) -> None:
47
+ self._result_generation += 1
48
+ self._description = None
49
+ self._column_map = {}
50
+ self._column_map_lower = None
51
+ self._column_names = None
52
+ self._uuid_str_indices = None
53
+
54
+ def _initialize_result_metadata(self) -> None:
55
+ with translate_py_core_exceptions():
56
+ description = self._py_core_async_cursor.description
57
+ if description is None:
58
+ self._clear_result_metadata()
59
+ return
60
+
61
+ settings = get_settings()
62
+ self._description = [
63
+ ((column[0].lower() if settings.lowercase else column[0]), *column[1:])
64
+ for column in description
65
+ ]
66
+ self._column_names = tuple(column[0] for column in self._description)
67
+ self._column_map = {column[0]: index for index, column in enumerate(self._description)}
68
+ self._column_map_lower = (
69
+ {name.lower(): index for name, index in self._column_map.items()}
70
+ if settings.lowercase
71
+ else None
72
+ )
73
+ self._uuid_str_indices = (
74
+ tuple(index for index, column in enumerate(self._description) if column[1] is uuid.UUID)
75
+ if not settings.native_uuid
76
+ else None
77
+ )
78
+
79
+ def _reset_fetch_tracking(self) -> None:
80
+ self._fetched_row_count = 0
81
+ self._fetch_rowcount = None
82
+
83
+ @asynccontextmanager
84
+ async def _result_transition(self):
85
+ async with self._result_transition_lock:
86
+ self._result_ready.clear()
87
+ try:
88
+ yield
89
+ finally:
90
+ self._result_ready.set()
91
+
92
+ async def _wait_for_result_publication(self) -> None:
93
+ while True:
94
+ await self._result_ready.wait()
95
+ if self._result_ready.is_set():
96
+ return
97
+
98
+ def _reconcile_failed_result_operation(self, operation: str, error: BaseException) -> None:
99
+ if is_connection_busy(error):
100
+ return
101
+ self._reset_fetch_tracking()
102
+ self._clear_result_metadata()
103
+ try:
104
+ self._initialize_result_metadata()
105
+ except Exception as error:
106
+ logger.debug("AsyncCursor.%s: metadata recovery failed: %s", operation, error)
107
+
108
+ def _check_closed(self) -> None:
109
+ if self._closed or (self._connection is not None and self._connection.closed):
110
+ message = "Cursor is closed" if self._closed else "Connection is closed"
111
+ with translate_py_core_exceptions():
112
+ raise RuntimeError(message)
113
+
114
+ def _record_fetch(self, generation: int, count: int, exhausted: bool) -> None:
115
+ if generation != self._result_generation:
116
+ return
117
+ if count:
118
+ self._fetched_row_count += count
119
+ self._fetch_rowcount = self._fetched_row_count
120
+ elif exhausted and self._fetched_row_count == 0:
121
+ self._fetch_rowcount = 0
122
+
123
+ async def execute(
124
+ self,
125
+ operation: str,
126
+ *parameters: Any,
127
+ use_prepare: bool = True,
128
+ reset_cursor: bool = True,
129
+ ) -> "_AsyncCursor":
130
+ async with self._result_transition():
131
+ return await async_execute.execute(
132
+ self,
133
+ operation,
134
+ *parameters,
135
+ use_prepare=use_prepare,
136
+ reset_cursor=reset_cursor,
137
+ )
138
+
139
+ async def executemany(
140
+ self,
141
+ operation: str,
142
+ seq_of_parameters: Sequence[Sequence[Any]] | Sequence[Mapping[str, Any]],
143
+ *,
144
+ use_prepare: bool = True,
145
+ ) -> None:
146
+ async with self._result_transition():
147
+ await async_execute.executemany(
148
+ self,
149
+ operation,
150
+ seq_of_parameters,
151
+ use_prepare=use_prepare,
152
+ )
153
+
154
+ async def fetchone(self) -> Row | None:
155
+ return await async_fetch.fetchone(self)
156
+
157
+ async def fetchmany(self, size: Optional[int] = None) -> list[Row]:
158
+ return await async_fetch.fetchmany(self, size)
159
+
160
+ async def fetchall(self) -> list[Row]:
161
+ return await async_fetch.fetchall(self)
162
+
163
+ async def nextset(self) -> bool:
164
+ async with self._result_transition():
165
+ try:
166
+ with translate_py_core_exceptions():
167
+ has_next = await self._py_core_async_cursor.nextset()
168
+ except (Exception, asyncio.CancelledError) as error:
169
+ self._reconcile_failed_result_operation("nextset", error)
170
+ raise
171
+ self._reset_fetch_tracking()
172
+ self._clear_result_metadata()
173
+ if has_next:
174
+ self._initialize_result_metadata()
175
+ return has_next
176
+
177
+ async def close(self) -> None:
178
+ logger.debug("AsyncCursor.close: starting")
179
+ async with self._result_transition():
180
+ with translate_py_core_exceptions():
181
+ close_awaitable = self._py_core_async_cursor.close()
182
+ close_accepted = True
183
+ try:
184
+ with translate_py_core_exceptions():
185
+ await close_awaitable
186
+ except (Exception, asyncio.CancelledError) as error:
187
+ if is_connection_busy(error):
188
+ close_accepted = False
189
+ raise
190
+ finally:
191
+ if close_accepted:
192
+ self._closed = True
193
+ self._reset_fetch_tracking()
194
+ self._clear_result_metadata()
195
+ logger.debug("AsyncCursor.close: completed")
196
+
197
+ def setinputsizes(self, sizes: Any) -> None:
198
+ with translate_py_core_exceptions():
199
+ self._py_core_async_cursor.setinputsizes(sizes)
200
+
201
+ @property
202
+ def timeout(self) -> int:
203
+ with translate_py_core_exceptions():
204
+ return self._py_core_async_cursor.timeout
205
+
206
+ @property
207
+ def description(self) -> Any:
208
+ return self._description
209
+
210
+ @property
211
+ def rowcount(self) -> int:
212
+ if self._fetch_rowcount is not None:
213
+ return self._fetch_rowcount
214
+ with translate_py_core_exceptions():
215
+ return self._py_core_async_cursor.rowcount
216
+
217
+ @property
218
+ def arraysize(self) -> int:
219
+ with translate_py_core_exceptions():
220
+ return self._py_core_async_cursor.arraysize
221
+
222
+ @arraysize.setter
223
+ def arraysize(self, value: int) -> None:
224
+ with translate_py_core_exceptions():
225
+ self._py_core_async_cursor.arraysize = value
@@ -0,0 +1,113 @@
1
+ """Asynchronous statement execution through mssql-py-core."""
2
+
3
+ import asyncio
4
+ from collections.abc import Mapping, Sequence
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from ..exceptions import ProgrammingError
8
+ from ..logging import logger
9
+ from ..row import Row
10
+ from .exception_translator import is_connection_busy, translate_py_core_exceptions
11
+
12
+ if TYPE_CHECKING:
13
+ from .async_cursor import _AsyncCursor # pyright: ignore[reportPrivateUsage]
14
+
15
+
16
+ def _get_py_core_async_cursor(cursor: "_AsyncCursor") -> Any:
17
+ return cursor._py_core_async_cursor # pyright: ignore[reportPrivateUsage]
18
+
19
+
20
+ def _is_non_mutating_rejection(error: BaseException) -> bool:
21
+ """Return whether py-core guarantees rejection before result-state mutation."""
22
+ if is_connection_busy(error):
23
+ return True
24
+ return isinstance(error, (TypeError, KeyError)) or (
25
+ isinstance(error, ProgrammingError) and isinstance(error.__cause__, TypeError)
26
+ )
27
+
28
+
29
+ def _reconcile_failed_execution(cursor: "_AsyncCursor", error: BaseException) -> None:
30
+ if _is_non_mutating_rejection(error):
31
+ return
32
+ cursor._reset_fetch_tracking() # pyright: ignore[reportPrivateUsage]
33
+ cursor._clear_result_metadata() # pyright: ignore[reportPrivateUsage]
34
+ try:
35
+ cursor._initialize_result_metadata() # pyright: ignore[reportPrivateUsage]
36
+ except Exception as error:
37
+ logger.debug("Async execution metadata recovery failed: %s", error)
38
+
39
+
40
+ async def execute(
41
+ cursor: "_AsyncCursor",
42
+ operation: str,
43
+ *parameters: Any,
44
+ use_prepare: bool = True,
45
+ reset_cursor: bool = True,
46
+ ) -> "_AsyncCursor":
47
+ """Execute a statement using the py-core async cursor."""
48
+ if len(parameters) == 1 and isinstance(parameters[0], (tuple, list, Row)):
49
+ parameters = tuple(parameters[0])
50
+
51
+ logger.debug(
52
+ "AsyncCursor.execute: starting; param_count=%d; use_prepare=%s; reset_cursor=%s",
53
+ len(parameters),
54
+ use_prepare,
55
+ reset_cursor,
56
+ )
57
+ with translate_py_core_exceptions():
58
+ execute_awaitable = _get_py_core_async_cursor(cursor).execute(
59
+ operation,
60
+ *parameters,
61
+ use_prepare=use_prepare,
62
+ reset_cursor=reset_cursor,
63
+ )
64
+ try:
65
+ with translate_py_core_exceptions():
66
+ await execute_awaitable
67
+ except (Exception, asyncio.CancelledError) as error:
68
+ _reconcile_failed_execution(cursor, error)
69
+ raise
70
+ cursor._reset_fetch_tracking() # pyright: ignore[reportPrivateUsage]
71
+ cursor._clear_result_metadata() # pyright: ignore[reportPrivateUsage]
72
+ cursor._initialize_result_metadata() # pyright: ignore[reportPrivateUsage]
73
+ description = cursor.description
74
+ logger.debug(
75
+ "AsyncCursor.execute: completed; rowcount=%d; column_count=%d; has_result_set=%s",
76
+ cursor.rowcount,
77
+ len(description) if description is not None else 0,
78
+ description is not None,
79
+ )
80
+ return cursor
81
+
82
+
83
+ async def executemany(
84
+ cursor: "_AsyncCursor",
85
+ operation: str,
86
+ seq_of_parameters: Sequence[Sequence[Any]] | Sequence[Mapping[str, Any]],
87
+ *,
88
+ use_prepare: bool = True,
89
+ ) -> None:
90
+ """Execute a statement for every parameter row using the py-core async cursor."""
91
+ cursor._check_closed() # pyright: ignore[reportPrivateUsage]
92
+ batch_count = len(seq_of_parameters)
93
+ logger.debug(
94
+ "AsyncCursor.executemany: starting; batch_count=%d; use_prepare=%s",
95
+ batch_count,
96
+ use_prepare,
97
+ )
98
+ with translate_py_core_exceptions():
99
+ executemany_awaitable = _get_py_core_async_cursor(cursor).executemany(
100
+ operation,
101
+ seq_of_parameters,
102
+ use_prepare=use_prepare,
103
+ )
104
+ try:
105
+ with translate_py_core_exceptions():
106
+ await executemany_awaitable
107
+ except (Exception, asyncio.CancelledError) as error:
108
+ _reconcile_failed_execution(cursor, error)
109
+ raise
110
+ cursor._reset_fetch_tracking() # pyright: ignore[reportPrivateUsage]
111
+ cursor._clear_result_metadata() # pyright: ignore[reportPrivateUsage]
112
+ cursor._initialize_result_metadata() # pyright: ignore[reportPrivateUsage]
113
+ logger.debug("AsyncCursor.executemany: completed; rowcount=%d", cursor.rowcount)
@@ -0,0 +1,153 @@
1
+ """Asynchronous result fetching through mssql-py-core."""
2
+
3
+ import asyncio
4
+ import logging
5
+
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ from ..exceptions import ProgrammingError
9
+ from ..logging import logger
10
+ from ..row import Row
11
+ from .async_execute import _get_py_core_async_cursor
12
+ from .exception_translator import (
13
+ is_connection_busy,
14
+ translate_py_core_exception,
15
+ translate_py_core_exceptions,
16
+ )
17
+
18
+ if TYPE_CHECKING:
19
+ from .async_cursor import _AsyncCursor # pyright: ignore[reportPrivateUsage]
20
+
21
+ _ResultSnapshot = tuple[
22
+ int,
23
+ dict[str, int],
24
+ tuple[int, ...] | None,
25
+ dict[str, int] | None,
26
+ tuple[str, ...] | None,
27
+ ]
28
+
29
+
30
+ def _snapshot_result(cursor: "_AsyncCursor") -> _ResultSnapshot:
31
+ return (
32
+ cursor._result_generation, # pyright: ignore[reportPrivateUsage]
33
+ cursor._column_map, # pyright: ignore[reportPrivateUsage]
34
+ cursor._uuid_str_indices, # pyright: ignore[reportPrivateUsage]
35
+ cursor._column_map_lower, # pyright: ignore[reportPrivateUsage]
36
+ cursor._column_names, # pyright: ignore[reportPrivateUsage]
37
+ )
38
+
39
+
40
+ def _reconcile_failed_fetch(
41
+ cursor: "_AsyncCursor", generation: int, operation: str, error: BaseException
42
+ ) -> None:
43
+ if generation != cursor._result_generation: # pyright: ignore[reportPrivateUsage]
44
+ return
45
+ if is_connection_busy(error):
46
+ return
47
+ logger.debug("AsyncCursor.%s: invalidating result state after fetch failure", operation)
48
+ cursor._reset_fetch_tracking() # pyright: ignore[reportPrivateUsage]
49
+ cursor._clear_result_metadata() # pyright: ignore[reportPrivateUsage]
50
+
51
+
52
+ def _wrap_row(snapshot: _ResultSnapshot, values: tuple[Any, ...]) -> Row:
53
+ _, column_map, uuid_str_indices, column_map_lower, column_names = snapshot
54
+ if not uuid_str_indices:
55
+ return Row._fast_create(list(values), column_map, None, column_map_lower, column_names) # pyright: ignore[reportPrivateUsage]
56
+ return Row(
57
+ list(values),
58
+ column_map,
59
+ uuid_str_indices=uuid_str_indices,
60
+ column_map_lower=column_map_lower,
61
+ column_names=column_names,
62
+ )
63
+
64
+
65
+ def _wrap_rows(snapshot: _ResultSnapshot, rows: list[tuple[Any, ...]]) -> list[Row]:
66
+ _, column_map, uuid_str_indices, column_map_lower, column_names = snapshot
67
+ if uuid_str_indices:
68
+ return [_wrap_row(snapshot, row) for row in rows]
69
+ create = Row._fast_create # pyright: ignore[reportPrivateUsage]
70
+ return [create(list(row), column_map, None, column_map_lower, column_names) for row in rows]
71
+
72
+
73
+ async def fetchone(cursor: "_AsyncCursor") -> Row | None:
74
+ """Fetch the next row through the py-core async cursor."""
75
+ logger.debug("AsyncCursor.fetchone: starting")
76
+ if not cursor._result_ready.is_set(): # pyright: ignore[reportPrivateUsage]
77
+ await cursor._wait_for_result_publication() # pyright: ignore[reportPrivateUsage]
78
+ snapshot = _snapshot_result(cursor)
79
+ try:
80
+ try:
81
+ row = await _get_py_core_async_cursor(cursor).fetchone()
82
+ except Exception as error:
83
+ translated = translate_py_core_exception(error)
84
+ if translated is error:
85
+ raise
86
+ raise translated from error
87
+ except (Exception, asyncio.CancelledError) as error:
88
+ _reconcile_failed_fetch(cursor, snapshot[0], "fetchone", error)
89
+ raise
90
+ cursor._record_fetch( # pyright: ignore[reportPrivateUsage]
91
+ snapshot[0], row is not None, row is None
92
+ )
93
+ if logger.isEnabledFor(logging.DEBUG):
94
+ logger.debug(
95
+ "AsyncCursor.fetchone: completed; row_found=%s; rowcount=%d",
96
+ row is not None,
97
+ cursor.rowcount,
98
+ )
99
+ return None if row is None else _wrap_row(snapshot, row)
100
+
101
+
102
+ async def fetchmany(cursor: "_AsyncCursor", size: int | None = None) -> list[Row]:
103
+ """Fetch up to size rows, using cursor arraysize when size is omitted."""
104
+ if not cursor._result_ready.is_set(): # pyright: ignore[reportPrivateUsage]
105
+ await cursor._wait_for_result_publication() # pyright: ignore[reportPrivateUsage]
106
+ cursor._check_closed() # pyright: ignore[reportPrivateUsage]
107
+ requested_size = cursor.arraysize if size is None else size
108
+ logger.debug("AsyncCursor.fetchmany: starting; requested_size=%s", requested_size)
109
+ if requested_size <= 0:
110
+ if cursor.description is None:
111
+ raise ProgrammingError("Async operation failed", "No active result set")
112
+ logger.debug("AsyncCursor.fetchmany: completed; row_count=0; rowcount=%d", cursor.rowcount)
113
+ return []
114
+ snapshot = _snapshot_result(cursor)
115
+ with translate_py_core_exceptions():
116
+ if size is None:
117
+ fetch_awaitable = _get_py_core_async_cursor(cursor).fetchmany()
118
+ else:
119
+ fetch_awaitable = _get_py_core_async_cursor(cursor).fetchmany(size)
120
+ try:
121
+ with translate_py_core_exceptions():
122
+ rows = await fetch_awaitable
123
+ except (Exception, asyncio.CancelledError) as error:
124
+ _reconcile_failed_fetch(cursor, snapshot[0], "fetchmany", error)
125
+ raise
126
+ cursor._record_fetch(snapshot[0], len(rows), not rows) # pyright: ignore[reportPrivateUsage]
127
+ logger.debug(
128
+ "AsyncCursor.fetchmany: completed; row_count=%d; rowcount=%d",
129
+ len(rows),
130
+ cursor.rowcount,
131
+ )
132
+ return _wrap_rows(snapshot, rows)
133
+
134
+
135
+ async def fetchall(cursor: "_AsyncCursor") -> list[Row]:
136
+ """Fetch all remaining rows through the py-core async cursor."""
137
+ logger.debug("AsyncCursor.fetchall: starting")
138
+ if not cursor._result_ready.is_set(): # pyright: ignore[reportPrivateUsage]
139
+ await cursor._wait_for_result_publication() # pyright: ignore[reportPrivateUsage]
140
+ snapshot = _snapshot_result(cursor)
141
+ try:
142
+ with translate_py_core_exceptions():
143
+ rows = await _get_py_core_async_cursor(cursor).fetchall()
144
+ except (Exception, asyncio.CancelledError) as error:
145
+ _reconcile_failed_fetch(cursor, snapshot[0], "fetchall", error)
146
+ raise
147
+ cursor._record_fetch(snapshot[0], len(rows), not rows) # pyright: ignore[reportPrivateUsage]
148
+ logger.debug(
149
+ "AsyncCursor.fetchall: completed; row_count=%d; rowcount=%d",
150
+ len(rows),
151
+ cursor.rowcount,
152
+ )
153
+ return _wrap_rows(snapshot, rows)