singlestoredb 1.16.1__py3-none-any.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 (183) hide show
  1. singlestoredb/__init__.py +75 -0
  2. singlestoredb/ai/__init__.py +2 -0
  3. singlestoredb/ai/chat.py +139 -0
  4. singlestoredb/ai/embeddings.py +128 -0
  5. singlestoredb/alchemy/__init__.py +90 -0
  6. singlestoredb/apps/__init__.py +3 -0
  7. singlestoredb/apps/_cloud_functions.py +90 -0
  8. singlestoredb/apps/_config.py +72 -0
  9. singlestoredb/apps/_connection_info.py +18 -0
  10. singlestoredb/apps/_dashboards.py +47 -0
  11. singlestoredb/apps/_process.py +32 -0
  12. singlestoredb/apps/_python_udfs.py +100 -0
  13. singlestoredb/apps/_stdout_supress.py +30 -0
  14. singlestoredb/apps/_uvicorn_util.py +36 -0
  15. singlestoredb/auth.py +245 -0
  16. singlestoredb/config.py +484 -0
  17. singlestoredb/connection.py +1487 -0
  18. singlestoredb/converters.py +950 -0
  19. singlestoredb/docstring/__init__.py +33 -0
  20. singlestoredb/docstring/attrdoc.py +126 -0
  21. singlestoredb/docstring/common.py +230 -0
  22. singlestoredb/docstring/epydoc.py +267 -0
  23. singlestoredb/docstring/google.py +412 -0
  24. singlestoredb/docstring/numpydoc.py +562 -0
  25. singlestoredb/docstring/parser.py +100 -0
  26. singlestoredb/docstring/py.typed +1 -0
  27. singlestoredb/docstring/rest.py +256 -0
  28. singlestoredb/docstring/tests/__init__.py +1 -0
  29. singlestoredb/docstring/tests/_pydoctor.py +21 -0
  30. singlestoredb/docstring/tests/test_epydoc.py +729 -0
  31. singlestoredb/docstring/tests/test_google.py +1007 -0
  32. singlestoredb/docstring/tests/test_numpydoc.py +1100 -0
  33. singlestoredb/docstring/tests/test_parse_from_object.py +109 -0
  34. singlestoredb/docstring/tests/test_parser.py +248 -0
  35. singlestoredb/docstring/tests/test_rest.py +547 -0
  36. singlestoredb/docstring/tests/test_util.py +70 -0
  37. singlestoredb/docstring/util.py +141 -0
  38. singlestoredb/exceptions.py +120 -0
  39. singlestoredb/functions/__init__.py +16 -0
  40. singlestoredb/functions/decorator.py +201 -0
  41. singlestoredb/functions/dtypes.py +1793 -0
  42. singlestoredb/functions/ext/__init__.py +1 -0
  43. singlestoredb/functions/ext/arrow.py +375 -0
  44. singlestoredb/functions/ext/asgi.py +2133 -0
  45. singlestoredb/functions/ext/json.py +420 -0
  46. singlestoredb/functions/ext/mmap.py +413 -0
  47. singlestoredb/functions/ext/rowdat_1.py +724 -0
  48. singlestoredb/functions/ext/timer.py +89 -0
  49. singlestoredb/functions/ext/utils.py +218 -0
  50. singlestoredb/functions/signature.py +1578 -0
  51. singlestoredb/functions/typing/__init__.py +41 -0
  52. singlestoredb/functions/typing/numpy.py +20 -0
  53. singlestoredb/functions/typing/pandas.py +2 -0
  54. singlestoredb/functions/typing/polars.py +2 -0
  55. singlestoredb/functions/typing/pyarrow.py +2 -0
  56. singlestoredb/functions/utils.py +421 -0
  57. singlestoredb/fusion/__init__.py +11 -0
  58. singlestoredb/fusion/graphql.py +213 -0
  59. singlestoredb/fusion/handler.py +916 -0
  60. singlestoredb/fusion/handlers/__init__.py +0 -0
  61. singlestoredb/fusion/handlers/export.py +525 -0
  62. singlestoredb/fusion/handlers/files.py +690 -0
  63. singlestoredb/fusion/handlers/job.py +660 -0
  64. singlestoredb/fusion/handlers/models.py +250 -0
  65. singlestoredb/fusion/handlers/stage.py +502 -0
  66. singlestoredb/fusion/handlers/utils.py +324 -0
  67. singlestoredb/fusion/handlers/workspace.py +956 -0
  68. singlestoredb/fusion/registry.py +249 -0
  69. singlestoredb/fusion/result.py +399 -0
  70. singlestoredb/http/__init__.py +27 -0
  71. singlestoredb/http/connection.py +1267 -0
  72. singlestoredb/magics/__init__.py +34 -0
  73. singlestoredb/magics/run_personal.py +137 -0
  74. singlestoredb/magics/run_shared.py +134 -0
  75. singlestoredb/management/__init__.py +9 -0
  76. singlestoredb/management/billing_usage.py +148 -0
  77. singlestoredb/management/cluster.py +462 -0
  78. singlestoredb/management/export.py +295 -0
  79. singlestoredb/management/files.py +1102 -0
  80. singlestoredb/management/inference_api.py +105 -0
  81. singlestoredb/management/job.py +887 -0
  82. singlestoredb/management/manager.py +373 -0
  83. singlestoredb/management/organization.py +226 -0
  84. singlestoredb/management/region.py +169 -0
  85. singlestoredb/management/utils.py +423 -0
  86. singlestoredb/management/workspace.py +1927 -0
  87. singlestoredb/mysql/__init__.py +177 -0
  88. singlestoredb/mysql/_auth.py +298 -0
  89. singlestoredb/mysql/charset.py +214 -0
  90. singlestoredb/mysql/connection.py +2032 -0
  91. singlestoredb/mysql/constants/CLIENT.py +38 -0
  92. singlestoredb/mysql/constants/COMMAND.py +32 -0
  93. singlestoredb/mysql/constants/CR.py +78 -0
  94. singlestoredb/mysql/constants/ER.py +474 -0
  95. singlestoredb/mysql/constants/EXTENDED_TYPE.py +3 -0
  96. singlestoredb/mysql/constants/FIELD_TYPE.py +48 -0
  97. singlestoredb/mysql/constants/FLAG.py +15 -0
  98. singlestoredb/mysql/constants/SERVER_STATUS.py +10 -0
  99. singlestoredb/mysql/constants/VECTOR_TYPE.py +6 -0
  100. singlestoredb/mysql/constants/__init__.py +0 -0
  101. singlestoredb/mysql/converters.py +271 -0
  102. singlestoredb/mysql/cursors.py +896 -0
  103. singlestoredb/mysql/err.py +92 -0
  104. singlestoredb/mysql/optionfile.py +20 -0
  105. singlestoredb/mysql/protocol.py +450 -0
  106. singlestoredb/mysql/tests/__init__.py +19 -0
  107. singlestoredb/mysql/tests/base.py +126 -0
  108. singlestoredb/mysql/tests/conftest.py +37 -0
  109. singlestoredb/mysql/tests/test_DictCursor.py +132 -0
  110. singlestoredb/mysql/tests/test_SSCursor.py +141 -0
  111. singlestoredb/mysql/tests/test_basic.py +452 -0
  112. singlestoredb/mysql/tests/test_connection.py +851 -0
  113. singlestoredb/mysql/tests/test_converters.py +58 -0
  114. singlestoredb/mysql/tests/test_cursor.py +141 -0
  115. singlestoredb/mysql/tests/test_err.py +16 -0
  116. singlestoredb/mysql/tests/test_issues.py +514 -0
  117. singlestoredb/mysql/tests/test_load_local.py +75 -0
  118. singlestoredb/mysql/tests/test_nextset.py +88 -0
  119. singlestoredb/mysql/tests/test_optionfile.py +27 -0
  120. singlestoredb/mysql/tests/thirdparty/__init__.py +6 -0
  121. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
  122. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +323 -0
  123. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +865 -0
  124. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +110 -0
  125. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +224 -0
  126. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +101 -0
  127. singlestoredb/mysql/times.py +23 -0
  128. singlestoredb/notebook/__init__.py +16 -0
  129. singlestoredb/notebook/_objects.py +213 -0
  130. singlestoredb/notebook/_portal.py +352 -0
  131. singlestoredb/py.typed +0 -0
  132. singlestoredb/pytest.py +352 -0
  133. singlestoredb/server/__init__.py +0 -0
  134. singlestoredb/server/docker.py +452 -0
  135. singlestoredb/server/free_tier.py +267 -0
  136. singlestoredb/tests/__init__.py +0 -0
  137. singlestoredb/tests/alltypes.sql +307 -0
  138. singlestoredb/tests/alltypes_no_nulls.sql +208 -0
  139. singlestoredb/tests/empty.sql +0 -0
  140. singlestoredb/tests/ext_funcs/__init__.py +702 -0
  141. singlestoredb/tests/local_infile.csv +3 -0
  142. singlestoredb/tests/test.ipynb +18 -0
  143. singlestoredb/tests/test.sql +680 -0
  144. singlestoredb/tests/test2.ipynb +18 -0
  145. singlestoredb/tests/test2.sql +1 -0
  146. singlestoredb/tests/test_basics.py +1332 -0
  147. singlestoredb/tests/test_config.py +318 -0
  148. singlestoredb/tests/test_connection.py +3103 -0
  149. singlestoredb/tests/test_dbapi.py +27 -0
  150. singlestoredb/tests/test_exceptions.py +45 -0
  151. singlestoredb/tests/test_ext_func.py +1472 -0
  152. singlestoredb/tests/test_ext_func_data.py +1101 -0
  153. singlestoredb/tests/test_fusion.py +1527 -0
  154. singlestoredb/tests/test_http.py +288 -0
  155. singlestoredb/tests/test_management.py +1599 -0
  156. singlestoredb/tests/test_plugin.py +33 -0
  157. singlestoredb/tests/test_results.py +171 -0
  158. singlestoredb/tests/test_types.py +132 -0
  159. singlestoredb/tests/test_udf.py +737 -0
  160. singlestoredb/tests/test_udf_returns.py +459 -0
  161. singlestoredb/tests/test_vectorstore.py +51 -0
  162. singlestoredb/tests/test_xdict.py +333 -0
  163. singlestoredb/tests/utils.py +141 -0
  164. singlestoredb/types.py +373 -0
  165. singlestoredb/utils/__init__.py +0 -0
  166. singlestoredb/utils/config.py +950 -0
  167. singlestoredb/utils/convert_rows.py +69 -0
  168. singlestoredb/utils/debug.py +13 -0
  169. singlestoredb/utils/dtypes.py +205 -0
  170. singlestoredb/utils/events.py +65 -0
  171. singlestoredb/utils/mogrify.py +151 -0
  172. singlestoredb/utils/results.py +585 -0
  173. singlestoredb/utils/xdict.py +425 -0
  174. singlestoredb/vectorstore.py +192 -0
  175. singlestoredb/warnings.py +5 -0
  176. singlestoredb-1.16.1.dist-info/METADATA +165 -0
  177. singlestoredb-1.16.1.dist-info/RECORD +183 -0
  178. singlestoredb-1.16.1.dist-info/WHEEL +5 -0
  179. singlestoredb-1.16.1.dist-info/entry_points.txt +2 -0
  180. singlestoredb-1.16.1.dist-info/licenses/LICENSE +201 -0
  181. singlestoredb-1.16.1.dist-info/top_level.txt +3 -0
  182. sqlx/__init__.py +4 -0
  183. sqlx/magic.py +113 -0
@@ -0,0 +1,896 @@
1
+ # type: ignore
2
+ import re
3
+ from collections import namedtuple
4
+
5
+ from . import err
6
+ from ..connection import Cursor as BaseCursor
7
+ from ..utils import results
8
+ from ..utils.debug import log_query
9
+ from ..utils.results import get_schema
10
+
11
+ try:
12
+ from pydantic import BaseModel
13
+ has_pydantic = True
14
+ except ImportError:
15
+ has_pydantic = False
16
+
17
+
18
+ #: Regular expression for :meth:`Cursor.executemany`.
19
+ #: executemany only supports simple bulk insert.
20
+ #: You can use it to load large dataset.
21
+ RE_INSERT_VALUES = re.compile(
22
+ r'\s*((?:INSERT|REPLACE)\b.+\bVALUES?\s*)'
23
+ + r'(\(\s*(?:%s|%\(.+\)s)\s*(?:,\s*(?:%s|%\(.+\)s)\s*)*\))'
24
+ + r'(\s*(?:ON DUPLICATE.*)?);?\s*\Z',
25
+ re.IGNORECASE | re.DOTALL,
26
+ )
27
+
28
+
29
+ class Cursor(BaseCursor):
30
+ """
31
+ This is the object used to interact with the database.
32
+
33
+ Do not create an instance of a Cursor yourself. Call
34
+ connection.Connection.cursor().
35
+
36
+ See `Cursor <https://www.python.org/dev/peps/pep-0249/#cursor-objects>`_ in
37
+ the specification.
38
+
39
+ Parameters
40
+ ----------
41
+ connection : Connection
42
+ The connection the cursor is associated with.
43
+
44
+ """
45
+
46
+ #: Max statement size which :meth:`executemany` generates.
47
+ #:
48
+ #: Max size of allowed statement is max_allowed_packet - packet_header_size.
49
+ #: Default value of max_allowed_packet is 1048576.
50
+ max_stmt_length = 1024000
51
+
52
+ def __init__(self, connection):
53
+ self._connection = connection
54
+ self.warning_count = 0
55
+ self._description = None
56
+ self._format_schema = None
57
+ self._rownumber = 0
58
+ self.rowcount = -1
59
+ self.arraysize = 1
60
+ self._executed = None
61
+ self._result = None
62
+ self._rows = None
63
+ self.lastrowid = None
64
+
65
+ @property
66
+ def messages(self):
67
+ # TODO
68
+ return []
69
+
70
+ @property
71
+ def description(self):
72
+ return self._description
73
+
74
+ @property
75
+ def _schema(self):
76
+ return self._format_schema
77
+
78
+ @property
79
+ def connection(self):
80
+ return self._connection
81
+
82
+ @property
83
+ def rownumber(self):
84
+ return self._rownumber
85
+
86
+ def close(self):
87
+ """Closing a cursor just exhausts all remaining data."""
88
+ conn = self._connection
89
+ if conn is None:
90
+ return
91
+ try:
92
+ while self.nextset():
93
+ pass
94
+ finally:
95
+ self._connection = None
96
+
97
+ @property
98
+ def open(self) -> bool:
99
+ conn = self._connection
100
+ if conn is None:
101
+ return False
102
+ return True
103
+
104
+ def is_connected(self):
105
+ return self.open
106
+
107
+ def __enter__(self):
108
+ return self
109
+
110
+ def __exit__(self, *exc_info):
111
+ del exc_info
112
+ self.close()
113
+
114
+ def _get_db(self):
115
+ if not self._connection:
116
+ raise err.ProgrammingError('Cursor closed')
117
+ return self._connection
118
+
119
+ def _check_executed(self):
120
+ if not self._executed:
121
+ raise err.ProgrammingError('execute() first')
122
+
123
+ def _conv_row(self, row):
124
+ return row
125
+
126
+ def setinputsizes(self, *args):
127
+ """Does nothing, required by DB API."""
128
+
129
+ def setoutputsizes(self, *args):
130
+ """Does nothing, required by DB API."""
131
+
132
+ setoutputsize = setoutputsizes
133
+
134
+ def _nextset(self, unbuffered=False):
135
+ """Get the next query set."""
136
+ conn = self._get_db()
137
+ current_result = self._result
138
+ if current_result is None or current_result is not conn._result:
139
+ return None
140
+ if not current_result.has_next:
141
+ return None
142
+ self._result = None
143
+ self._clear_result()
144
+ conn.next_result(unbuffered=unbuffered)
145
+ self._do_get_result()
146
+ return True
147
+
148
+ def nextset(self):
149
+ return self._nextset(False)
150
+
151
+ def _escape_args(self, args, conn):
152
+ dtype = type(args)
153
+ literal = conn.literal
154
+ if dtype is tuple or dtype is list or isinstance(args, (tuple, list)):
155
+ return tuple(literal(arg) for arg in args)
156
+ elif dtype is dict or isinstance(args, dict):
157
+ return {key: literal(val) for (key, val) in args.items()}
158
+ elif has_pydantic and isinstance(args, BaseModel):
159
+ return {key: literal(val) for (key, val) in args.model_dump().items()}
160
+ # If it's not a dictionary let's try escaping it anyways.
161
+ # Worst case it will throw a Value error
162
+ return conn.escape(args)
163
+
164
+ def mogrify(self, query, args=None):
165
+ """
166
+ Returns the exact string sent to the database by calling the execute() method.
167
+
168
+ This method follows the extension to the DB API 2.0 followed by Psycopg.
169
+
170
+ Parameters
171
+ ----------
172
+ query : str
173
+ Query to mogrify.
174
+ args : Sequence[Any] or Dict[str, Any] or Any, optional
175
+ Parameters used with query. (optional)
176
+
177
+ Returns
178
+ -------
179
+ str : The query with argument binding applied.
180
+
181
+ """
182
+ conn = self._get_db()
183
+
184
+ if args:
185
+ query = query % self._escape_args(args, conn)
186
+
187
+ return query
188
+
189
+ def execute(self, query, args=None, infile_stream=None):
190
+ """
191
+ Execute a query.
192
+
193
+ If args is a list or tuple, :1, :2, etc. can be used as a
194
+ placeholder in the query. If args is a dict, :name can be used
195
+ as a placeholder in the query.
196
+
197
+ Parameters
198
+ ----------
199
+ query : str
200
+ Query to execute.
201
+ args : Sequence[Any] or Dict[str, Any] or Any, optional
202
+ Parameters used with query. (optional)
203
+ infile_stream : io.BytesIO or Iterator[bytes], optional
204
+ Data stream for ``LOCAL INFILE`` statements
205
+
206
+ Returns
207
+ -------
208
+ int : Number of affected rows.
209
+
210
+ """
211
+ while self.nextset():
212
+ pass
213
+
214
+ log_query(query, args)
215
+
216
+ query = self.mogrify(query, args)
217
+
218
+ result = self._query(query, infile_stream=infile_stream)
219
+ self._executed = query
220
+ return result
221
+
222
+ def executemany(self, query, args=None):
223
+ """
224
+ Run several data against one query.
225
+
226
+ This method improves performance on multiple-row INSERT and
227
+ REPLACE. Otherwise it is equivalent to looping over args with
228
+ execute().
229
+
230
+ Parameters
231
+ ----------
232
+ query : str,
233
+ Query to execute.
234
+ args : Sequnce[Any], optional
235
+ Sequence of sequences or mappings. It is used as parameter.
236
+
237
+ Returns
238
+ -------
239
+ int : Number of rows affected, if any.
240
+
241
+ """
242
+ if args is None or len(args) == 0:
243
+ return
244
+
245
+ m = RE_INSERT_VALUES.match(query)
246
+ if m:
247
+ q_prefix = m.group(1) % ()
248
+ q_values = m.group(2).rstrip()
249
+ q_postfix = m.group(3) or ''
250
+ assert q_values[0] == '(' and q_values[-1] == ')'
251
+ return self._do_execute_many(
252
+ q_prefix,
253
+ q_values,
254
+ q_postfix,
255
+ args,
256
+ self.max_stmt_length,
257
+ self._get_db().encoding,
258
+ )
259
+
260
+ self.rowcount = sum(self.execute(query, arg) for arg in args)
261
+ return self.rowcount
262
+
263
+ def _do_execute_many(
264
+ self, prefix, values, postfix, args, max_stmt_length, encoding,
265
+ ):
266
+ conn = self._get_db()
267
+ escape = self._escape_args
268
+ if isinstance(prefix, str):
269
+ prefix = prefix.encode(encoding)
270
+ if isinstance(postfix, str):
271
+ postfix = postfix.encode(encoding)
272
+ sql = bytearray(prefix)
273
+ # Detect dataframes
274
+ if hasattr(args, 'itertuples'):
275
+ args = args.itertuples(index=False)
276
+ else:
277
+ args = iter(args)
278
+ v = values % escape(next(args), conn)
279
+ if isinstance(v, str):
280
+ v = v.encode(encoding, 'surrogateescape')
281
+ sql += v
282
+ rows = 0
283
+ for arg in args:
284
+ v = values % escape(arg, conn)
285
+ if type(v) is str or isinstance(v, str):
286
+ v = v.encode(encoding, 'surrogateescape')
287
+ if len(sql) + len(v) + len(postfix) + 1 > max_stmt_length:
288
+ rows += self.execute(sql + postfix)
289
+ sql = bytearray(prefix)
290
+ else:
291
+ sql += b','
292
+ sql += v
293
+ rows += self.execute(sql + postfix)
294
+ self.rowcount = rows
295
+ return rows
296
+
297
+ def callproc(self, procname, args=()):
298
+ """
299
+ Execute stored procedure procname with args.
300
+
301
+ Compatibility warning: PEP-249 specifies that any modified
302
+ parameters must be returned. This is currently impossible
303
+ as they are only available by storing them in a server
304
+ variable and then retrieved by a query. Since stored
305
+ procedures return zero or more result sets, there is no
306
+ reliable way to get at OUT or INOUT parameters via callproc.
307
+ The server variables are named @_procname_n, where procname
308
+ is the parameter above and n is the position of the parameter
309
+ (from zero). Once all result sets generated by the procedure
310
+ have been fetched, you can issue a SELECT @_procname_0, ...
311
+ query using .execute() to get any OUT or INOUT values.
312
+
313
+ Compatibility warning: The act of calling a stored procedure
314
+ itself creates an empty result set. This appears after any
315
+ result sets generated by the procedure. This is non-standard
316
+ behavior with respect to the DB-API. Be sure to use nextset()
317
+ to advance through all result sets; otherwise you may get
318
+ disconnected.
319
+
320
+ Parameters
321
+ ----------
322
+ procname : str
323
+ Name of procedure to execute on server.
324
+ args : Sequence[Any], optional
325
+ Sequence of parameters to use with procedure.
326
+
327
+ Returns
328
+ -------
329
+ Sequence[Any] : The original args.
330
+
331
+ """
332
+ conn = self._get_db()
333
+ if args:
334
+ fmt = f'@_{procname}_%d=%s'
335
+ self._query(
336
+ 'SET %s'
337
+ % ','.join(
338
+ fmt % (index, conn.escape(arg)) for index, arg in enumerate(args)
339
+ ),
340
+ )
341
+ self.nextset()
342
+
343
+ q = 'CALL {}({})'.format(
344
+ procname,
345
+ ','.join(['@_%s_%d' % (procname, i) for i in range(len(args))]),
346
+ )
347
+ self._query(q)
348
+ self._executed = q
349
+ return args
350
+
351
+ def fetchone(self):
352
+ """Fetch the next row."""
353
+ self._check_executed()
354
+ return self._unchecked_fetchone()
355
+
356
+ def _unchecked_fetchone(self):
357
+ """Fetch the next row."""
358
+ if self._rows is None or self._rownumber >= len(self._rows):
359
+ return None
360
+ result = self._rows[self._rownumber]
361
+ self._rownumber += 1
362
+ return result
363
+
364
+ def fetchmany(self, size=None):
365
+ """Fetch several rows."""
366
+ self._check_executed()
367
+ if self._rows is None:
368
+ self.warning_count = self._result.warning_count
369
+ return ()
370
+ end = self._rownumber + (size or self.arraysize)
371
+ result = self._rows[self._rownumber: end]
372
+ self._rownumber = min(end, len(self._rows))
373
+ return result
374
+
375
+ def fetchall(self):
376
+ """Fetch all the rows."""
377
+ self._check_executed()
378
+ if self._rows is None:
379
+ return ()
380
+ if self._rownumber:
381
+ result = self._rows[self._rownumber:]
382
+ else:
383
+ result = self._rows
384
+ self._rownumber = len(self._rows)
385
+ return result
386
+
387
+ def scroll(self, value, mode='relative'):
388
+ self._check_executed()
389
+ if mode == 'relative':
390
+ r = self._rownumber + value
391
+ elif mode == 'absolute':
392
+ r = value
393
+ else:
394
+ raise err.ProgrammingError('unknown scroll mode %s' % mode)
395
+
396
+ if not (0 <= r < len(self._rows)):
397
+ raise IndexError('out of range')
398
+ self._rownumber = r
399
+
400
+ def _query(self, q, infile_stream=None):
401
+ conn = self._get_db()
402
+ self._clear_result()
403
+ conn.query(q, infile_stream=infile_stream)
404
+ self._do_get_result()
405
+ return self.rowcount
406
+
407
+ def _clear_result(self):
408
+ self._rownumber = 0
409
+ self._result = None
410
+
411
+ self.rowcount = 0
412
+ self.warning_count = 0
413
+ self._description = None
414
+ self._format_schema = None
415
+ self.lastrowid = None
416
+ self._rows = None
417
+
418
+ def _do_get_result(self):
419
+ conn = self._get_db()
420
+
421
+ self._result = result = conn._result
422
+
423
+ self.rowcount = result.affected_rows
424
+ self.warning_count = result.warning_count
425
+ # Affected rows is set to max int64 for compatibility with MySQLdb, but
426
+ # the DB-API requires this value to be -1. This happens in unbuffered mode.
427
+ if self.rowcount == 18446744073709551615:
428
+ self.rowcount = -1
429
+ self._description = result.description
430
+ if self._description:
431
+ self._format_schema = get_schema(
432
+ self.connection._results_type,
433
+ result.description,
434
+ )
435
+ self.lastrowid = result.insert_id
436
+ self._rows = result.rows
437
+
438
+ def __iter__(self):
439
+ self._check_executed()
440
+
441
+ def fetchall_unbuffered_gen(_unchecked_fetchone=self._unchecked_fetchone):
442
+ while True:
443
+ out = _unchecked_fetchone()
444
+ if out is not None:
445
+ yield out
446
+ else:
447
+ break
448
+ return fetchall_unbuffered_gen()
449
+
450
+ Warning = err.Warning
451
+ Error = err.Error
452
+ InterfaceError = err.InterfaceError
453
+ DatabaseError = err.DatabaseError
454
+ DataError = err.DataError
455
+ OperationalError = err.OperationalError
456
+ IntegrityError = err.IntegrityError
457
+ InternalError = err.InternalError
458
+ ProgrammingError = err.ProgrammingError
459
+ NotSupportedError = err.NotSupportedError
460
+
461
+
462
+ class CursorSV(Cursor):
463
+ """Cursor class for C extension."""
464
+
465
+
466
+ class ArrowCursorMixin:
467
+ """Fetch methods for Arrow Tables."""
468
+
469
+ def fetchone(self):
470
+ return results.results_to_arrow(
471
+ self.description, super().fetchone(), single=True, schema=self._schema,
472
+ )
473
+
474
+ def fetchall(self):
475
+ return results.results_to_arrow(
476
+ self.description, super().fetchall(), schema=self._schema,
477
+ )
478
+
479
+ def fetchall_unbuffered(self):
480
+ return results.results_to_arrow(
481
+ self.description, super().fetchall_unbuffered(), schema=self._schema,
482
+ )
483
+
484
+ def fetchmany(self, size=None):
485
+ return results.results_to_arrow(
486
+ self.description, super().fetchmany(size), schema=self._schema,
487
+ )
488
+
489
+
490
+ class ArrowCursor(ArrowCursorMixin, Cursor):
491
+ """A cursor which returns results as an Arrow Table."""
492
+
493
+
494
+ class ArrowCursorSV(ArrowCursorMixin, CursorSV):
495
+ """A cursor which returns results as an Arrow Table for C extension."""
496
+
497
+
498
+ class NumpyCursorMixin:
499
+ """Fetch methods for numpy arrays."""
500
+
501
+ def fetchone(self):
502
+ return results.results_to_numpy(
503
+ self.description, super().fetchone(), single=True, schema=self._schema,
504
+ )
505
+
506
+ def fetchall(self):
507
+ return results.results_to_numpy(
508
+ self.description, super().fetchall(), schema=self._schema,
509
+ )
510
+
511
+ def fetchall_unbuffered(self):
512
+ return results.results_to_numpy(
513
+ self.description, super().fetchall_unbuffered(), schema=self._schema,
514
+ )
515
+
516
+ def fetchmany(self, size=None):
517
+ return results.results_to_numpy(
518
+ self.description, super().fetchmany(size), schema=self._schema,
519
+ )
520
+
521
+
522
+ class NumpyCursor(NumpyCursorMixin, Cursor):
523
+ """A cursor which returns results as a numpy array."""
524
+
525
+
526
+ class NumpyCursorSV(NumpyCursorMixin, CursorSV):
527
+ """A cursor which returns results as a numpy array for C extension."""
528
+
529
+
530
+ class PandasCursorMixin:
531
+ """Fetch methods for pandas DataFrames."""
532
+
533
+ def fetchone(self):
534
+ return results.results_to_pandas(
535
+ self.description, super().fetchone(), single=True, schema=self._schema,
536
+ )
537
+
538
+ def fetchall(self):
539
+ return results.results_to_pandas(
540
+ self.description, super().fetchall(), schema=self._schema,
541
+ )
542
+
543
+ def fetchall_unbuffered(self):
544
+ return results.results_to_pandas(
545
+ self.description, super().fetchall_unbuffered(), schema=self._schema,
546
+ )
547
+
548
+ def fetchmany(self, size=None):
549
+ return results.results_to_pandas(
550
+ self.description, super().fetchmany(size), schema=self._schema,
551
+ )
552
+
553
+
554
+ class PandasCursor(PandasCursorMixin, Cursor):
555
+ """A cursor which returns results as a pandas DataFrame."""
556
+
557
+
558
+ class PandasCursorSV(PandasCursorMixin, CursorSV):
559
+ """A cursor which returns results as a pandas DataFrame for C extension."""
560
+
561
+
562
+ class PolarsCursorMixin:
563
+ """Fetch methods for polars DataFrames."""
564
+
565
+ def fetchone(self):
566
+ return results.results_to_polars(
567
+ self.description, super().fetchone(), single=True, schema=self._schema,
568
+ )
569
+
570
+ def fetchall(self):
571
+ return results.results_to_polars(
572
+ self.description, super().fetchall(), schema=self._schema,
573
+ )
574
+
575
+ def fetchall_unbuffered(self):
576
+ return results.results_to_polars(
577
+ self.description, super().fetchall_unbuffered(), schema=self._schema,
578
+ )
579
+
580
+ def fetchmany(self, size=None):
581
+ return results.results_to_polars(
582
+ self.description, super().fetchmany(size), schema=self._schema,
583
+ )
584
+
585
+
586
+ class PolarsCursor(PolarsCursorMixin, Cursor):
587
+ """A cursor which returns results as a polars DataFrame."""
588
+
589
+
590
+ class PolarsCursorSV(PolarsCursorMixin, CursorSV):
591
+ """A cursor which returns results as a polars DataFrame for C extension."""
592
+
593
+
594
+ class DictCursorMixin:
595
+ # You can override this to use OrderedDict or other dict-like types.
596
+ dict_type = dict
597
+
598
+ def _do_get_result(self):
599
+ super(DictCursorMixin, self)._do_get_result()
600
+ fields = []
601
+ if self._description:
602
+ for f in self._result.fields:
603
+ name = f.name
604
+ if name in fields:
605
+ name = f.table_name + '.' + name
606
+ fields.append(name)
607
+ self._fields = fields
608
+
609
+ if fields and self._rows:
610
+ self._rows = [self._conv_row(r) for r in self._rows]
611
+
612
+ def _conv_row(self, row):
613
+ if row is None:
614
+ return None
615
+ return self.dict_type(zip(self._fields, row))
616
+
617
+
618
+ class DictCursor(DictCursorMixin, Cursor):
619
+ """A cursor which returns results as a dictionary."""
620
+
621
+
622
+ class DictCursorSV(Cursor):
623
+ """A cursor which returns results as a dictionary for C extension."""
624
+
625
+
626
+ class NamedtupleCursorMixin:
627
+
628
+ def _do_get_result(self):
629
+ super(NamedtupleCursorMixin, self)._do_get_result()
630
+ fields = []
631
+ if self._description:
632
+ for f in self._result.fields:
633
+ name = f.name
634
+ if name in fields:
635
+ name = f.table_name + '.' + name
636
+ fields.append(name)
637
+ self._fields = fields
638
+ self._namedtuple = namedtuple('Row', self._fields, rename=True)
639
+
640
+ if fields and self._rows:
641
+ self._rows = [self._conv_row(r) for r in self._rows]
642
+
643
+ def _conv_row(self, row):
644
+ if row is None:
645
+ return None
646
+ return self._namedtuple(*row)
647
+
648
+
649
+ class NamedtupleCursor(NamedtupleCursorMixin, Cursor):
650
+ """A cursor which returns results in a named tuple."""
651
+
652
+
653
+ class NamedtupleCursorSV(Cursor):
654
+ """A cursor which returns results as a named tuple for C extension."""
655
+
656
+
657
+ class SSCursor(Cursor):
658
+ """
659
+ Unbuffered Cursor, mainly useful for queries that return a lot of data,
660
+ or for connections to remote servers over a slow network.
661
+
662
+ Instead of copying every row of data into a buffer, this will fetch
663
+ rows as needed. The upside of this is the client uses much less memory,
664
+ and rows are returned much faster when traveling over a slow network
665
+ or if the result set is very big.
666
+
667
+ There are limitations, though. The MySQL protocol doesn't support
668
+ returning the total number of rows, so the only way to tell how many rows
669
+ there are is to iterate over every row returned. Also, it currently isn't
670
+ possible to scroll backwards, as only the current row is held in memory.
671
+
672
+ """
673
+
674
+ def _conv_row(self, row):
675
+ return row
676
+
677
+ def close(self):
678
+ conn = self._connection
679
+ if conn is None:
680
+ return
681
+
682
+ if self._result is not None and self._result is conn._result:
683
+ self._result._finish_unbuffered_query()
684
+
685
+ try:
686
+ while self.nextset():
687
+ pass
688
+ finally:
689
+ self._connection = None
690
+
691
+ __del__ = close
692
+
693
+ def _query(self, q, infile_stream=None):
694
+ conn = self._get_db()
695
+ self._clear_result()
696
+ conn.query(q, unbuffered=True, infile_stream=infile_stream)
697
+ self._do_get_result()
698
+ return self.rowcount
699
+
700
+ def nextset(self):
701
+ return self._nextset(unbuffered=True)
702
+
703
+ def read_next(self):
704
+ """Read next row."""
705
+ return self._conv_row(self._result._read_rowdata_packet_unbuffered())
706
+
707
+ def fetchone(self):
708
+ """Fetch next row."""
709
+ self._check_executed()
710
+ return self._unchecked_fetchone()
711
+
712
+ def _unchecked_fetchone(self):
713
+ """Fetch next row."""
714
+ row = self.read_next()
715
+ if row is None:
716
+ self.warning_count = self._result.warning_count
717
+ return None
718
+ self._rownumber += 1
719
+ return row
720
+
721
+ def fetchall(self):
722
+ """
723
+ Fetch all, as per MySQLdb.
724
+
725
+ Pretty useless for large queries, as it is buffered.
726
+ See fetchall_unbuffered(), if you want an unbuffered
727
+ generator version of this method.
728
+
729
+ """
730
+ return list(self.fetchall_unbuffered())
731
+
732
+ def fetchall_unbuffered(self):
733
+ """
734
+ Fetch all, implemented as a generator.
735
+
736
+ This is not a standard DB-API operation, however, it doesn't make
737
+ sense to return everything in a list, as that would use ridiculous
738
+ memory for large result sets.
739
+
740
+ """
741
+ self._check_executed()
742
+
743
+ def fetchall_unbuffered_gen(_unchecked_fetchone=self._unchecked_fetchone):
744
+ while True:
745
+ out = _unchecked_fetchone()
746
+ if out is not None:
747
+ yield out
748
+ else:
749
+ break
750
+ return fetchall_unbuffered_gen()
751
+
752
+ def __iter__(self):
753
+ return self.fetchall_unbuffered()
754
+
755
+ def fetchmany(self, size=None):
756
+ """Fetch many."""
757
+ self._check_executed()
758
+ if size is None:
759
+ size = self.arraysize
760
+
761
+ rows = []
762
+ for i in range(size):
763
+ row = self.read_next()
764
+ if row is None:
765
+ self.warning_count = self._result.warning_count
766
+ break
767
+ rows.append(row)
768
+ self._rownumber += 1
769
+ return rows
770
+
771
+ def scroll(self, value, mode='relative'):
772
+ self._check_executed()
773
+
774
+ if mode == 'relative':
775
+ if value < 0:
776
+ raise err.NotSupportedError(
777
+ 'Backwards scrolling not supported by this cursor',
778
+ )
779
+
780
+ for _ in range(value):
781
+ self.read_next()
782
+ self._rownumber += value
783
+ elif mode == 'absolute':
784
+ if value < self._rownumber:
785
+ raise err.NotSupportedError(
786
+ 'Backwards scrolling not supported by this cursor',
787
+ )
788
+
789
+ end = value - self._rownumber
790
+ for _ in range(end):
791
+ self.read_next()
792
+ self._rownumber = value
793
+ else:
794
+ raise err.ProgrammingError('unknown scroll mode %s' % mode)
795
+
796
+
797
+ class SSCursorSV(SSCursor):
798
+ """An unbuffered cursor for use with PyMySQLsv."""
799
+
800
+ def _unchecked_fetchone(self):
801
+ """Fetch next row."""
802
+ row = self._result._read_rowdata_packet_unbuffered(1)
803
+ if row is None:
804
+ return None
805
+ self._rownumber += 1
806
+ return row
807
+
808
+ def fetchone(self):
809
+ """Fetch next row."""
810
+ self._check_executed()
811
+ return self._unchecked_fetchone()
812
+
813
+ def fetchmany(self, size=None):
814
+ """Fetch many."""
815
+ self._check_executed()
816
+ if size is None:
817
+ size = self.arraysize
818
+ out = self._result._read_rowdata_packet_unbuffered(size)
819
+ if out is None:
820
+ return []
821
+ if size == 1:
822
+ self._rownumber += 1
823
+ return [out]
824
+ self._rownumber += len(out)
825
+ return out
826
+
827
+ def scroll(self, value, mode='relative'):
828
+ self._check_executed()
829
+
830
+ if mode == 'relative':
831
+ if value < 0:
832
+ raise err.NotSupportedError(
833
+ 'Backwards scrolling not supported by this cursor',
834
+ )
835
+
836
+ self._result._read_rowdata_packet_unbuffered(value)
837
+ self._rownumber += value
838
+ elif mode == 'absolute':
839
+ if value < self._rownumber:
840
+ raise err.NotSupportedError(
841
+ 'Backwards scrolling not supported by this cursor',
842
+ )
843
+
844
+ end = value - self._rownumber
845
+ self._result._read_rowdata_packet_unbuffered(end)
846
+ self._rownumber = value
847
+ else:
848
+ raise err.ProgrammingError('unknown scroll mode %s' % mode)
849
+
850
+
851
+ class SSDictCursor(DictCursorMixin, SSCursor):
852
+ """An unbuffered cursor, which returns results as a dictionary."""
853
+
854
+
855
+ class SSDictCursorSV(SSCursorSV):
856
+ """An unbuffered cursor for the C extension, which returns a dictionary."""
857
+
858
+
859
+ class SSNamedtupleCursor(NamedtupleCursorMixin, SSCursor):
860
+ """An unbuffered cursor, which returns results as a named tuple."""
861
+
862
+
863
+ class SSNamedtupleCursorSV(SSCursorSV):
864
+ """An unbuffered cursor for the C extension, which returns results as named tuple."""
865
+
866
+
867
+ class SSArrowCursor(ArrowCursorMixin, SSCursor):
868
+ """An unbuffered cursor, which returns results as an Arrow Table."""
869
+
870
+
871
+ class SSArrowCursorSV(ArrowCursorMixin, SSCursorSV):
872
+ """An unbuffered cursor, which returns results as an Arrow Table (accelerated)."""
873
+
874
+
875
+ class SSNumpyCursor(NumpyCursorMixin, SSCursor):
876
+ """An unbuffered cursor, which returns results as a numpy array."""
877
+
878
+
879
+ class SSNumpyCursorSV(NumpyCursorMixin, SSCursorSV):
880
+ """An unbuffered cursor, which returns results as a numpy array (accelerated)."""
881
+
882
+
883
+ class SSPandasCursor(PandasCursorMixin, SSCursor):
884
+ """An unbuffered cursor, which returns results as a pandas DataFrame."""
885
+
886
+
887
+ class SSPandasCursorSV(PandasCursorMixin, SSCursorSV):
888
+ """An unbuffered cursor, which returns results as a pandas DataFrame (accelerated)."""
889
+
890
+
891
+ class SSPolarsCursor(PolarsCursorMixin, SSCursor):
892
+ """An unbuffered cursor, which returns results as a polars DataFrame."""
893
+
894
+
895
+ class SSPolarsCursorSV(PolarsCursorMixin, SSCursorSV):
896
+ """An unbuffered cursor, which returns results as a polars DataFrame (accelerated)."""