iris-pex-embedded-python 2.3.16__py3-none-any.whl → 2.3.18__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.

Potentially problematic release.


This version of iris-pex-embedded-python might be problematic. Click here for more details.

Files changed (120) hide show
  1. grongier/iris/Grongier/PEX/BusinessOperation.cls +33 -0
  2. grongier/iris/Grongier/PEX/BusinessProcess.cls +100 -0
  3. grongier/iris/Grongier/PEX/BusinessService.cls +33 -0
  4. grongier/iris/Grongier/PEX/Common.cls +133 -0
  5. grongier/iris/Grongier/PEX/Director.cls +15 -0
  6. grongier/iris/Grongier/PEX/Duplex/Operation.cls +27 -0
  7. grongier/iris/Grongier/PEX/Duplex/Process.cls +211 -0
  8. grongier/iris/Grongier/PEX/Duplex/Service.cls +9 -0
  9. grongier/iris/Grongier/PEX/InboundAdapter.cls +22 -0
  10. grongier/iris/Grongier/PEX/Message.cls +126 -0
  11. grongier/iris/Grongier/PEX/OutboundAdapter.cls +34 -0
  12. grongier/iris/Grongier/PEX/PickleMessage.cls +58 -0
  13. grongier/iris/Grongier/PEX/PrivateSession/Duplex.cls +247 -0
  14. grongier/iris/Grongier/PEX/PrivateSession/Message/Ack.cls +32 -0
  15. grongier/iris/Grongier/PEX/PrivateSession/Message/Poll.cls +32 -0
  16. grongier/iris/Grongier/PEX/PrivateSession/Message/Start.cls +32 -0
  17. grongier/iris/Grongier/PEX/PrivateSession/Message/Stop.cls +48 -0
  18. grongier/iris/Grongier/PEX/Python.cls +27 -0
  19. grongier/iris/Grongier/PEX/Test.cls +62 -0
  20. grongier/iris/Grongier/PEX/Utils.cls +312 -0
  21. grongier/iris/__init__.py +0 -0
  22. grongier/pex/_business_host.py +9 -1
  23. grongier/pex/_cli.py +8 -2
  24. grongier/pex/data/PEX/BusinessOperation.cls +35 -0
  25. grongier/pex/data/PEX/BusinessProcess.cls +113 -0
  26. grongier/pex/data/PEX/BusinessService.cls +35 -0
  27. grongier/pex/data/PEX/Common.cls +146 -0
  28. grongier/pex/data/PEX/Director.cls +57 -0
  29. grongier/pex/data/PEX/Duplex/Operation.cls +29 -0
  30. grongier/pex/data/PEX/Duplex/Process.cls +229 -0
  31. grongier/pex/data/PEX/Duplex/Service.cls +9 -0
  32. grongier/pex/data/PEX/InboundAdapter.cls +22 -0
  33. grongier/pex/data/PEX/Message.cls +128 -0
  34. grongier/pex/data/PEX/OutboundAdapter.cls +36 -0
  35. grongier/pex/data/PEX/PickleMessage.cls +58 -0
  36. grongier/pex/data/PEX/PrivateSession/Duplex.cls +260 -0
  37. grongier/pex/data/PEX/PrivateSession/Message/Ack.cls +32 -0
  38. grongier/pex/data/PEX/PrivateSession/Message/Poll.cls +32 -0
  39. grongier/pex/data/PEX/PrivateSession/Message/Start.cls +32 -0
  40. grongier/pex/data/PEX/PrivateSession/Message/Stop.cls +48 -0
  41. grongier/pex/data/PEX/Test.cls +62 -0
  42. grongier/pex/data/PEX/Utils.cls +413 -0
  43. intersystems_iris/_BufferReader.py +10 -0
  44. intersystems_iris/_BufferWriter.py +32 -0
  45. intersystems_iris/_ConnectionInformation.py +54 -0
  46. intersystems_iris/_ConnectionParameters.py +18 -0
  47. intersystems_iris/_Constant.py +38 -0
  48. intersystems_iris/_DBList.py +500 -0
  49. intersystems_iris/_Device.py +69 -0
  50. intersystems_iris/_GatewayContext.py +25 -0
  51. intersystems_iris/_GatewayException.py +4 -0
  52. intersystems_iris/_GatewayUtility.py +74 -0
  53. intersystems_iris/_IRIS.py +1294 -0
  54. intersystems_iris/_IRISConnection.py +516 -0
  55. intersystems_iris/_IRISEmbedded.py +85 -0
  56. intersystems_iris/_IRISGlobalNode.py +273 -0
  57. intersystems_iris/_IRISGlobalNodeView.py +25 -0
  58. intersystems_iris/_IRISIterator.py +143 -0
  59. intersystems_iris/_IRISList.py +360 -0
  60. intersystems_iris/_IRISNative.py +208 -0
  61. intersystems_iris/_IRISOREF.py +4 -0
  62. intersystems_iris/_IRISObject.py +424 -0
  63. intersystems_iris/_IRISReference.py +133 -0
  64. intersystems_iris/_InStream.py +149 -0
  65. intersystems_iris/_LegacyIterator.py +135 -0
  66. intersystems_iris/_ListItem.py +15 -0
  67. intersystems_iris/_ListReader.py +84 -0
  68. intersystems_iris/_ListWriter.py +157 -0
  69. intersystems_iris/_LogFileStream.py +115 -0
  70. intersystems_iris/_MessageHeader.py +51 -0
  71. intersystems_iris/_OutStream.py +25 -0
  72. intersystems_iris/_PrintStream.py +65 -0
  73. intersystems_iris/_PythonGateway.py +850 -0
  74. intersystems_iris/_SharedMemorySocket.py +87 -0
  75. intersystems_iris/__init__.py +79 -0
  76. intersystems_iris/__main__.py +7 -0
  77. intersystems_iris/dbapi/_Column.py +56 -0
  78. intersystems_iris/dbapi/_DBAPI.py +2587 -0
  79. intersystems_iris/dbapi/_Descriptor.py +46 -0
  80. intersystems_iris/dbapi/_IRISStream.py +65 -0
  81. intersystems_iris/dbapi/_Message.py +158 -0
  82. intersystems_iris/dbapi/_Parameter.py +169 -0
  83. intersystems_iris/dbapi/_ParameterCollection.py +141 -0
  84. intersystems_iris/dbapi/_ResultSetRow.py +338 -0
  85. intersystems_iris/dbapi/_SQLType.py +32 -0
  86. intersystems_iris/dbapi/__init__.py +0 -0
  87. intersystems_iris/dbapi/preparser/_PreParser.py +1671 -0
  88. intersystems_iris/dbapi/preparser/_Scanner.py +391 -0
  89. intersystems_iris/dbapi/preparser/_Token.py +81 -0
  90. intersystems_iris/dbapi/preparser/_TokenList.py +251 -0
  91. intersystems_iris/dbapi/preparser/__init__.py +0 -0
  92. intersystems_iris/pex/_BusinessHost.py +101 -0
  93. intersystems_iris/pex/_BusinessOperation.py +105 -0
  94. intersystems_iris/pex/_BusinessProcess.py +214 -0
  95. intersystems_iris/pex/_BusinessService.py +95 -0
  96. intersystems_iris/pex/_Common.py +228 -0
  97. intersystems_iris/pex/_Director.py +24 -0
  98. intersystems_iris/pex/_IRISBusinessOperation.py +5 -0
  99. intersystems_iris/pex/_IRISBusinessService.py +18 -0
  100. intersystems_iris/pex/_IRISInboundAdapter.py +5 -0
  101. intersystems_iris/pex/_IRISOutboundAdapter.py +17 -0
  102. intersystems_iris/pex/_InboundAdapter.py +57 -0
  103. intersystems_iris/pex/_Message.py +6 -0
  104. intersystems_iris/pex/_OutboundAdapter.py +46 -0
  105. intersystems_iris/pex/__init__.py +25 -0
  106. iris/__init__.py +33 -0
  107. iris/iris_ipm.py +40 -0
  108. iris/iris_site.py +13 -0
  109. iris/irisbuiltins.py +97 -0
  110. iris/irisloader.py +199 -0
  111. {iris_pex_embedded_python-2.3.16.dist-info → iris_pex_embedded_python-2.3.18.dist-info}/METADATA +3 -4
  112. iris_pex_embedded_python-2.3.18.dist-info/RECORD +153 -0
  113. {iris_pex_embedded_python-2.3.16.dist-info → iris_pex_embedded_python-2.3.18.dist-info}/WHEEL +1 -1
  114. iris_pex_embedded_python-2.3.18.dist-info/top_level.txt +4 -0
  115. irisnative/_IRISNative.py +9 -0
  116. irisnative/__init__.py +10 -0
  117. iris_pex_embedded_python-2.3.16.dist-info/RECORD +0 -43
  118. iris_pex_embedded_python-2.3.16.dist-info/top_level.txt +0 -1
  119. {iris_pex_embedded_python-2.3.16.dist-info → iris_pex_embedded_python-2.3.18.dist-info}/LICENSE +0 -0
  120. {iris_pex_embedded_python-2.3.16.dist-info → iris_pex_embedded_python-2.3.18.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,2587 @@
1
+ import struct
2
+ import copy
3
+ import enum
4
+ import copy
5
+ import decimal
6
+ import intersystems_iris
7
+ from collections import namedtuple
8
+ from . import _Message
9
+ import intersystems_iris.dbapi._Parameter
10
+ import intersystems_iris.dbapi._Column
11
+ from ._ResultSetRow import _ResultSetRow
12
+ import intersystems_iris.dbapi._ParameterCollection
13
+ import intersystems_iris.dbapi.preparser._PreParser
14
+ from intersystems_iris.dbapi._Parameter import ParameterMode
15
+ from intersystems_iris.dbapi.preparser._PreParser import StatementType, MultiValuesInsert
16
+ from intersystems_iris._IRISConnection import Feature
17
+ from intersystems_iris._InStream import _InStream
18
+ from intersystems_iris.dbapi._IRISStream import (
19
+ IRISStream,
20
+ IRISBinaryStream,
21
+ )
22
+ from ._SQLType import SQLType
23
+
24
+ from .._IRISNative import connect as native_connect
25
+ from .._IRISEmbedded import _IRISEmbedded
26
+
27
+
28
+ def NotImplementedErrorDBAPI(msg=None):
29
+ import traceback
30
+
31
+ if msg is None:
32
+ traceback.print_stack()
33
+ msg = "Coming soon to an IRIS DB API near you!"
34
+ return NotImplementedError(msg)
35
+
36
+
37
+ def embedded_connect(*args, hostname=None, port=None, namespace=None, username=None, password=None, **kw):
38
+ connection = _IRISEmbedded()
39
+ connection.connect(hostname, port, namespace, username, password, **kw)
40
+ return connection
41
+
42
+
43
+ def connect(*args, embedded=False, hostname=None, port=None, namespace=None, username=None, password=None, **kw):
44
+ try:
45
+ if not embedded:
46
+ return native_connect(
47
+ *args, hostname=hostname, port=port, namespace=namespace, username=username, password=password, **kw
48
+ )
49
+ else:
50
+ return embedded_connect(
51
+ *args, hostname=hostname, port=port, namespace=namespace, username=username, password=password, **kw
52
+ )
53
+ except Exception as e:
54
+ raise OperationalError(e)
55
+
56
+
57
+ class ServerReturnType(enum.IntEnum):
58
+ NO_RETURN_VALUE = 0
59
+ IGNORE_RETURN_VALUE = 1
60
+ HAS_RETURN_VALUE = 2
61
+ NULL_RETURN_VALUE = 3
62
+
63
+
64
+ class CursorType(enum.IntEnum):
65
+ DEFAULT = 0
66
+ PREPARED = 1
67
+ CALLABLE = 2
68
+
69
+
70
+ # api globals, methods, classes, etc.
71
+ # globals
72
+ apilevel = "2.0"
73
+ threadsafety = 0
74
+ paramstyle = "qmark"
75
+
76
+
77
+ class _BaseCursor:
78
+ embedded = False
79
+
80
+ def __init__(self, connection):
81
+ self._connection = connection
82
+
83
+ self.statement = None
84
+ self._parsed_statement = None
85
+
86
+ self._columns = None
87
+ self._rowcount = -1
88
+ self.arraysize = 1
89
+
90
+ self._result_set = None
91
+
92
+ self._rsrow = None
93
+ self._rownumber = 0
94
+ self._cursor_ptr = 0
95
+ self._scroll_flag = False
96
+
97
+ self.maxRowItemCount = 0
98
+
99
+ self._is_batch_update = False
100
+
101
+ self._exec_params = None
102
+ self._params = intersystems_iris.dbapi._ParameterCollection._ParameterCollection()
103
+ self._multiple_result_sets = False
104
+ self._mrs_done = False
105
+ self._has_return_value = 0
106
+ self._cursor_type = 0
107
+ self._parameter_list_mismatch_exception = False
108
+ self._fetch_done = False
109
+ self._output_parameter_list = None
110
+
111
+ self._sqlcode = None
112
+ self._lastrowid = None
113
+
114
+ self._closed = False
115
+
116
+ def __enter__(self):
117
+ return self
118
+
119
+ def __exit__(self, type, value, traceback):
120
+ self._connection.rollback()
121
+ self.close()
122
+
123
+ def __iter__(self):
124
+ if self._result_set == None:
125
+ raise InterfaceError(
126
+ "Either execute has not yet been called, or the previous call of execute did not return a result set"
127
+ )
128
+ return self
129
+
130
+ def __next__(self):
131
+ row = self.fetchone()
132
+ if row:
133
+ return row
134
+ raise StopIteration
135
+
136
+ # non-api methods and classes
137
+ def isClosed(self):
138
+ return self._closed
139
+
140
+ def setinputsizes(self, sizes):
141
+ raise NotImplementedErrorDBAPI()
142
+
143
+ def setoutputsize(size, column=None):
144
+ raise NotImplementedErrorDBAPI()
145
+
146
+ @property
147
+ def sqlcode(self):
148
+ if self._closed:
149
+ raise InterfaceError("Cursor is closed")
150
+ return 0 if self._sqlcode is None else self._sqlcode
151
+
152
+ def close(self):
153
+ if self._closed:
154
+ return
155
+ self._columns = None
156
+ self._rowcount = -1
157
+ self.arraysize = 0
158
+
159
+ self._connection = None
160
+ self._in_message = None
161
+ self._out_message = None
162
+
163
+ self._result_set = None
164
+
165
+ if self._rsrow != None:
166
+ self._rsrow = None
167
+ self._cursor_ptr = 0
168
+ self._scroll_flag = False
169
+ self._warehouse = []
170
+ self._warehouse_dict = {}
171
+ self._last_row_in_warehouse_dict = -1
172
+ self._warehouse_dict_keys = []
173
+
174
+ self._params = None
175
+ self._parsed_statement = None
176
+ self._cursor_type = CursorType.DEFAULT
177
+ self._statementType = StatementType.UPDATE # default
178
+ self._paramInfo = None
179
+ self.statement = None
180
+ self.statementFeatureOption = Feature.optionNone
181
+ self._statement_id = None
182
+ self._sqlcode = None
183
+ self._current_wire = None
184
+ self._result_set = []
185
+ self._rs_index = -1
186
+
187
+ self._parameter_sets = 0
188
+ self._exec_params = None
189
+ self._is_batch_update = False
190
+ self._multiple_result_sets = False
191
+ self._mrs_done = False
192
+ self._fetch_done = False
193
+ self._parameter_list_mismatch_exception = False
194
+ if self._output_parameter_list != None:
195
+ self._output_parameter_list._clear_list()
196
+
197
+ self._closed = True
198
+
199
+ def _cleanup(self):
200
+ if self._rsrow != None:
201
+ self._rsrow = None
202
+ if self._params != None:
203
+ self._params._clear()
204
+ self._multiple_result_sets = False
205
+ self._mrs_done = False
206
+ self._fetch_done = False
207
+ self._parameter_list_mismatch_exception = False
208
+ self._parameter_sets = 0
209
+ self._rowcount = -1
210
+ self._exec_params = None
211
+ self._statementType = StatementType.UPDATE
212
+ self.statementFeatureOption = 0
213
+ self.maxRowItemCount = 0
214
+ self._is_batch_update = False
215
+
216
+ def _is_alive(self):
217
+ if self._closed:
218
+ raise InterfaceError("Cursor is closed")
219
+ if self._connection == None or self._connection.isClosed():
220
+ raise InterfaceError("Connection not open")
221
+
222
+ def direct_execute(self, operation, *params):
223
+ self._is_alive()
224
+
225
+ self.statement = operation
226
+ if len(params) == 1 and (isinstance(params[0], tuple) or isinstance(params[0], list)):
227
+ self.params = params[0]
228
+ else:
229
+ self.params = params
230
+ self._params.set_input_params(self.params)
231
+
232
+ self._cursor_type = CursorType.DEFAULT
233
+ self._cleanup()
234
+ self._preparse()
235
+
236
+ self._execute()
237
+ return self._rowcount
238
+
239
+ def execute(self, operation, params=()):
240
+ self._is_alive()
241
+
242
+ self.statement = operation
243
+ if params and not isinstance(params, list) and not isinstance(params, tuple):
244
+ params = (params,)
245
+ self.params = params if params is not None else ()
246
+ self._params.set_input_params(self.params)
247
+
248
+ self._cleanup()
249
+ try:
250
+ self._preparse()
251
+ except MultiValuesInsert as ex:
252
+ # convert to executemany
253
+ params = params or ex.params
254
+ params_count = int(len(params) / ex.rows)
255
+ new_params = [params[i : i + params_count] for i in range(0, len(params), params_count)]
256
+ return self.executemany(ex.query, new_params)
257
+ except Exception:
258
+ raise
259
+
260
+ if self._statementType == StatementType.UPDATE:
261
+ self._cursor_type = CursorType.PREPARED
262
+ self._prepare()
263
+ else:
264
+ self._cursor_type = CursorType.DEFAULT
265
+
266
+ self._execute()
267
+ return self._rowcount
268
+
269
+ def add_batch(self):
270
+ self._is_alive()
271
+
272
+ if self._params._array_bound:
273
+ if len(self._params._params_list) > 0:
274
+ cnt = 0
275
+ first = True
276
+ for i in range(self._params._user_parameters_size):
277
+ i = i + 1
278
+ cnt = len(self._params._get_user_param(i)._values)
279
+ if cnt > 1:
280
+ if first:
281
+ self._parameter_sets = cnt
282
+ first = False
283
+ elif self._parameter_sets != cnt:
284
+ raise Exception(
285
+ "Unmatched columnwise parameter values: "
286
+ + str(self._parameter_sets)
287
+ + " rows expected, but found only "
288
+ + str(cnt)
289
+ + " in "
290
+ + str(i)
291
+ + " parameter!"
292
+ )
293
+ if self._parameter_sets > 1:
294
+ return
295
+ self._parameter_sets = self._parameter_sets + 1
296
+
297
+ for param in self._params._params_list:
298
+ if param.mode != ParameterMode.REPLACED_LITERAL:
299
+ if len(param._values) != self._parameter_sets:
300
+ if self._parameter_sets != 1:
301
+ if len(param._values) + 1 == self._parameter_sets:
302
+ param._values.append(param._values[len(param._values) - 1])
303
+ continue
304
+
305
+ def executemany(self, operation, seq_of_params):
306
+ self._is_alive()
307
+ self._rowcount = 0
308
+
309
+ if not isinstance(seq_of_params, tuple) and not isinstance(seq_of_params, list):
310
+ seq_of_params = tuple(seq_of_params)
311
+
312
+ self.statement = operation
313
+ self.params = copy.deepcopy(seq_of_params)
314
+ self._params.set_input_params(self.params)
315
+
316
+ self._cursor_type = CursorType.PREPARED
317
+ self._cleanup()
318
+ self._is_batch_update = True
319
+
320
+ self._preparse()
321
+ self._prepare()
322
+
323
+ for row_num, param_row in enumerate(seq_of_params):
324
+ self.add_batch()
325
+
326
+ if self._parameter_sets == 0:
327
+ for param in self._params._params_list:
328
+ if param.value == "?":
329
+ raise ValueError("Missing value")
330
+ self._prepared_update_execute() # similar to executing a statement w/ literals
331
+ return
332
+
333
+ for param in self._params._params_list:
334
+ mode = param.mode
335
+ if mode == ParameterMode.INPUT_OUTPUT or mode == ParameterMode.OUTPUT:
336
+ raise ValueError("INOUT/OUT parameters not permitted")
337
+
338
+ self._prepared_update_execute()
339
+
340
+ return self._rowcount
341
+
342
+ def _process_sqlcode(self, sqlcode, message=None):
343
+ self._sqlcode = sqlcode
344
+ if sqlcode in [0, 100]:
345
+ return
346
+ if abs(sqlcode) in [108, 119, 121, 122]:
347
+ raise IntegrityError(message)
348
+ if abs(sqlcode) in [1, 12]:
349
+ raise OperationalError(message)
350
+ raise DatabaseError(message)
351
+
352
+ def _preparse(self):
353
+ csql = self._connection._pre_preparse_cache.get(self.statement)
354
+ if csql is not None:
355
+ self._has_return_value = csql._has_return_value
356
+ self._params = copy.deepcopy(csql._params)
357
+ self._params.set_input_params(self.params)
358
+ self._parsed_statement = csql._parsed_statement
359
+ self._statementType = csql._statementType
360
+ self._paramInfo = csql._paramInfo
361
+ return
362
+
363
+ count = 0
364
+ for i, item in enumerate(self.params):
365
+ if isinstance(item, list) or isinstance(item, tuple):
366
+ if not self._is_batch_update:
367
+ raise TypeError("Unsupported argument type: " + str(type(item)))
368
+ for ele in item:
369
+ if (not intersystems_iris._DBList._DBList._set_switcher.get(type(ele), None) and not issubclass(
370
+ type(ele), enum.Enum)):
371
+ raise TypeError("Unsupported argument type: " + str(type(ele)))
372
+ elif intersystems_iris._DBList._DBList._set_switcher.get(type(item), None) is None:
373
+ item = str(item)
374
+ # raise TypeError("Unsupported argument type: " + str(type(item)))
375
+ if i == 0:
376
+ count = len(item) if isinstance(item, list) or isinstance(item, tuple) else 1
377
+ else:
378
+ curr_count = len(item) if isinstance(item, list) or isinstance(item, tuple) else 1
379
+ if count != curr_count:
380
+ raise Exception("Parameter count does not match")
381
+
382
+ parser = intersystems_iris.dbapi.preparser._PreParser._PreParser(
383
+ self._connection._connection_info._delimited_ids, embedded=self.embedded
384
+ )
385
+ try:
386
+ pOut = parser.PreParse(self.statement, self._params)
387
+ except MultiValuesInsert:
388
+ raise
389
+ except Exception as e:
390
+ raise InterfaceError("Error parsing statement '" + self.statement + "':\n" + str(e))
391
+
392
+ if len(self.params) > 0:
393
+ item = self.params[0]
394
+ if (isinstance(item, list) or isinstance(item, tuple)) and not self._is_batch_update:
395
+ raise TypeError("Unsupported argument type: " + str(type(item)))
396
+
397
+ self._parsed_statement = pOut.sResult
398
+ self._statementType = pOut.p_eStmtType
399
+ self._paramInfo = parser.m_ParamInfo
400
+
401
+ if self._statementType == StatementType.CALL:
402
+ self._has_return_value = ServerReturnType.NO_RETURN_VALUE
403
+ elif self._statementType == StatementType.CALLWITHRESULT:
404
+ self._has_return_value = ServerReturnType.HAS_RETURN_VALUE
405
+
406
+ self._update_parameters()
407
+ self._connection._add_pre_preparse_cache(self.statement, self)
408
+
409
+ def _prepare(self):
410
+ notDDL = bool(
411
+ self._statementType != StatementType.DDL_ALTER_DROP and self._statementType != StatementType.DDL_OTHER
412
+ )
413
+
414
+ if notDDL and self._get_cached_info():
415
+ return
416
+ else:
417
+ self._prepare_new()
418
+
419
+ def _update_parameters(self):
420
+ count = self._paramInfo._list_data[0] # self._paramInfo.count()
421
+ if count == 0:
422
+ return
423
+
424
+ temp_list_data = self._paramInfo._list_data[1:]
425
+ param_info_count = int(len(temp_list_data) / 2)
426
+ if self._is_batch_update:
427
+ unknown_count = replaced_count = 0
428
+ for item in temp_list_data:
429
+ if item == "c":
430
+ replaced_count = replaced_count + 1
431
+ elif item == "?":
432
+ unknown_count = unknown_count + 1
433
+
434
+ if len(self.params) > 0:
435
+ item = self.params[0]
436
+ param_count = len(item) if isinstance(item, list) or isinstance(item, tuple) else len(self.params)
437
+ if param_count != unknown_count:
438
+ raise Exception(f"Parameter mismatch: {param_count}/{unknown_count}")
439
+ else:
440
+ if self._cursor_type == CursorType.CALLABLE:
441
+ i = 0
442
+ for param in self._params._params_list:
443
+ if param.mode == ParameterMode.RETURN_VALUE:
444
+ continue
445
+ else:
446
+ if len(self._params._params_list) == len(self.params) and self.params[i] == None:
447
+ param.mode = ParameterMode.OUTPUT
448
+ if len(self._params._params_list) > len(self.params):
449
+ if i >= len(self.params):
450
+ param.mode = ParameterMode.OUTPUT
451
+ else:
452
+ if self.params[i] == None:
453
+ param.mode = ParameterMode.OUTPUT
454
+ i += 1
455
+ return
456
+
457
+ if len(temp_list_data) > 0:
458
+ if count != param_info_count:
459
+ raise Exception("Parameter mismatch")
460
+
461
+ unknown_count = replaced_count = 0
462
+ for item in temp_list_data:
463
+ if item == "c":
464
+ replaced_count = replaced_count + 1
465
+ elif item == "?":
466
+ unknown_count = unknown_count + 1
467
+
468
+ if unknown_count != len(self.params):
469
+ raise Exception(
470
+ f"Incorrect number of parameters: {unknown_count}/{replaced_count}/{len(self.params)}"
471
+ )
472
+
473
+ def _is_not_default_or_replaced(self, param):
474
+ mode = param.mode
475
+ if (
476
+ mode != ParameterMode.REPLACED_LITERAL
477
+ and mode != ParameterMode.DEFAULT_PARAMETER
478
+ and mode != ParameterMode.INPUT
479
+ ):
480
+ raise Exception("Parameters not allowed in Cursor class")
481
+
482
+ def _validate_parameters(self):
483
+ if self._parameter_list_mismatch_exception and not self._params._has_bound_by_param_name:
484
+ raise Exception("Parameter list mismatch")
485
+ for param in self._params._params_list:
486
+ self._is_not_default_or_replaced(param)
487
+
488
+ def _validate_prepared_parameters(self):
489
+ if self._parameter_list_mismatch_exception and not self._params._has_bound_by_param_name:
490
+ raise Exception("Parameter list mismatch")
491
+ i = 0
492
+ if (
493
+ self._has_return_value == ServerReturnType.IGNORE_RETURN_VALUE
494
+ or self._has_return_value == ServerReturnType.NULL_RETURN_VALUE
495
+ ):
496
+ i = 1
497
+ for param in self._params._params_list:
498
+ if i == 1:
499
+ i = 0
500
+ continue
501
+ if param.mode == ParameterMode.UNKNOWN:
502
+ if self._params._has_named_parameters():
503
+ param.mode = ParameterMode.DEFAULT_PARAMETER
504
+ else:
505
+ raise Exception("Not all parameters bound/registered")
506
+
507
+ def _execute(self):
508
+ if self._closed:
509
+ raise InterfaceError("Cursor is closed")
510
+ if self._connection == None or self._connection.isClosed():
511
+ raise InterfaceError("Connection not open")
512
+
513
+ exec_switcher = {
514
+ StatementType.QUERY: self._execute_query,
515
+ StatementType.CALL: self._execute_update,
516
+ StatementType.STMT_USE: self._execute_update,
517
+ StatementType.UPDATE: self._execute_update,
518
+ StatementType.DDL_OTHER: self._execute_update,
519
+ StatementType.DDL_ALTER_DROP: self._execute_update,
520
+ }
521
+ exec_func = exec_switcher.get(self._statementType, None)
522
+ if exec_func is None:
523
+ raise NotImplementedErrorDBAPI(f"StatementType {self._statementType.name} not implemented")
524
+ else:
525
+ return exec_func()
526
+
527
+ def _prepare_stored_procedure(self):
528
+ if self._get_cached_info():
529
+ self._prepared_stored_procedure_execute()
530
+ pass
531
+
532
+ def _execute_stored_procedure(self):
533
+ if self._cursor_type == CursorType.DEFAULT:
534
+ if self._get_cached_info():
535
+ # found in client side cache - send SQ message
536
+ self._stored_procedure_update()
537
+ else:
538
+ # not found in client side cache - send DS message
539
+ self._send_direct_stored_procedure_request()
540
+ else:
541
+ self._stored_procedure_update()
542
+
543
+ def _execute_query(self):
544
+ self._fetch_done = False
545
+ if self._cursor_type == CursorType.DEFAULT:
546
+ if self._statementType not in [StatementType.QUERY, StatementType.CALL, StatementType.CALLWITHRESULT]:
547
+ raise Exception("Not a query")
548
+
549
+ if self._exec_params != None:
550
+ self._prepare_stored_procedure()
551
+ self._bind_exec_params()
552
+ if self._statementType in [StatementType.DIRECT_CALL_UPDATE, StatementType.PREPARED_CALL_UPDATE]:
553
+ raise Exception("Not a query")
554
+ elif self._statementType in [StatementType.DIRECT_CALL_QUERY, StatementType.PREPARED_CALL_QUERY]:
555
+ self._stored_procedure_query()
556
+ return
557
+
558
+ self._validate_parameters()
559
+
560
+ if self._statementType in [StatementType.CALL, StatementType.CALLWITHRESULT]:
561
+ self._execute_stored_procedure()
562
+ if not (self._statementType in [StatementType.DIRECT_CALL_QUERY, StatementType.PREPARED_CALL_QUERY]):
563
+ raise Exception("Not a query")
564
+ return
565
+
566
+ if self._get_cached_info():
567
+ # found in client side cache - send PQ message
568
+ self._prepared_query_execute()
569
+ else:
570
+ # not found in client side cache - send DQ message
571
+ self._send_direct_query_request()
572
+ else:
573
+ if (
574
+ self._statementType != StatementType.QUERY
575
+ and self._statementType != StatementType.PREPARED_CALL_QUERY
576
+ and self._statementType != StatementType.DIRECT_CALL_QUERY
577
+ ):
578
+ raise Exception("Not a query")
579
+
580
+ if self._exec_params != None:
581
+ self._bind_exec_params()
582
+ self._validate_prepared_parameters()
583
+
584
+ if self.statementFeatureOption & Feature.optionFastSelect == Feature.optionFastSelect:
585
+ self._rsrow = _ResultSetRow(self._connection, self._columns, self.maxRowItemCount)
586
+ else:
587
+ self._rsrow = _ResultSetRow(self._connection, self._columns, 0)
588
+
589
+ if self._cursor_type == CursorType.CALLABLE or self._statementType == StatementType.PREPARED_CALL_QUERY:
590
+ self._stored_procedure_query()
591
+ return
592
+ self._prepared_query_execute()
593
+
594
+ def _execute_update(self):
595
+ if self._cursor_type == CursorType.DEFAULT:
596
+ if self._statementType == StatementType.QUERY:
597
+ raise Exception("Not an update")
598
+
599
+ if self._exec_params != None:
600
+ self._prepare_stored_procedure()
601
+ self._bind_exec_params()
602
+ if self._statementType in [StatementType.DIRECT_CALL_UPDATE, StatementType.PREPARED_CALL_UPDATE]:
603
+ self._stored_procedure_query()
604
+ elif self._statementType in [StatementType.DIRECT_CALL_QUERY, StatementType.PREPARED_CALL_QUERY]:
605
+ raise Exception("Not an update")
606
+ return
607
+
608
+ self._validate_parameters()
609
+
610
+ if self._statementType == StatementType.CALL:
611
+ self._execute_stored_procedure()
612
+ if self._statementType == StatementType.DIRECT_CALL_QUERY:
613
+ raise Exception("Not an update")
614
+ return
615
+
616
+ notDDL = bool(
617
+ self._statementType != StatementType.DDL_ALTER_DROP and self._statementType != StatementType.DDL_OTHER
618
+ )
619
+
620
+ if notDDL and self._get_cached_info():
621
+ # found in client side cache - send PU message
622
+ self._prepared_update_execute()
623
+ else:
624
+ # not found in client side cache - send DU message
625
+ self._send_direct_update_request()
626
+ else:
627
+ if self._statementType == StatementType.QUERY or self._statementType == StatementType.PREPARED_CALL_QUERY:
628
+ raise Exception("Not an update")
629
+
630
+ if self._exec_params != None:
631
+ self._bind_exec_params()
632
+ self._validate_prepared_parameters()
633
+
634
+ if (
635
+ self._cursor_type == CursorType.CALLABLE
636
+ or self._statementType == StatementType.PREPARED_CALL_UPDATE
637
+ or self._statementType == StatementType.DIRECT_CALL_UPDATE
638
+ ):
639
+ self._stored_procedure_update()
640
+ return
641
+
642
+ self._prepared_update_execute()
643
+
644
+ def _query404(self):
645
+ with self._connection._lock:
646
+ self._validate_parameters()
647
+ self._send_direct_query_request()
648
+
649
+ def _update404(self):
650
+ with self._connection._lock:
651
+ self._validate_parameters()
652
+ self._send_direct_update_request()
653
+
654
+ # api properties and methods
655
+ @property
656
+ def description(self):
657
+ if self._statementType is StatementType.UPDATE:
658
+ return None
659
+
660
+ if self._columns is None:
661
+ return None
662
+
663
+ Column = namedtuple(
664
+ "Column",
665
+ [
666
+ "name",
667
+ "type_code",
668
+ "display_size",
669
+ "internal_size",
670
+ "precision",
671
+ "scale",
672
+ "null_ok",
673
+ ],
674
+ )
675
+
676
+ sequence = []
677
+ for column in self._columns:
678
+ sequence.append(
679
+ Column(
680
+ column.name,
681
+ column.type,
682
+ None,
683
+ None,
684
+ column.precision,
685
+ column.scale,
686
+ column.nullable,
687
+ )
688
+ )
689
+ return tuple(sequence)
690
+
691
+ # currently doesn't work for queries
692
+ @property
693
+ def rowcount(self):
694
+ return self._rowcount
695
+
696
+
697
+ # Cursor class
698
+ class Cursor(_BaseCursor):
699
+ def __init__(self, connection):
700
+ super().__init__(connection)
701
+
702
+ self._columns = None
703
+ self._rowcount = -1
704
+ self.arraysize = 1
705
+
706
+ self._in_message = intersystems_iris._InStream._InStream(connection)
707
+ self._out_message = intersystems_iris._OutStream._OutStream(connection)
708
+ self.statementFeatureOption = 0
709
+
710
+ self._result_set = None
711
+
712
+ self._rsrow = None
713
+ self._rownumber = 0
714
+ self._cursor_ptr = 0
715
+ self._scroll_flag = False
716
+ self._warehouse = list()
717
+
718
+ self._warehouse_dict = dict()
719
+ self._last_row_in_warehouse_dict = -1
720
+ self._warehouse_dict_keys = list()
721
+
722
+ self.maxRowItemCount = 0
723
+
724
+ self._parameter_sets = 0
725
+ self._exec_params = None
726
+ self._params = intersystems_iris.dbapi._ParameterCollection._ParameterCollection()
727
+ self._is_batch_update = False
728
+
729
+ self._exec_params = None
730
+ self._multiple_result_sets = False
731
+ self._mrs_done = False
732
+ self._has_return_value = 0
733
+ self._cursor_type = 0
734
+ self._parameter_list_mismatch_exception = False
735
+ self._fetch_done = False
736
+ self._output_parameter_list = None
737
+
738
+ self._lastrowid = None
739
+
740
+ self._closed = False
741
+
742
+ def _process_sqlcode(self, sqlcode, message=None):
743
+ if sqlcode in [0, 100]:
744
+ return
745
+ super()._process_sqlcode(sqlcode, self._get_error_info(sqlcode))
746
+
747
+ def _get_cached_info(self):
748
+ if not self._connection._preparedCache or not hasattr(self._connection._preparedCache, "__iter__"):
749
+ return False
750
+ if self._parsed_statement in self._connection._preparedCache:
751
+ self._prepare_cached(self._connection._preparedCache[self._parsed_statement])
752
+ return True
753
+ else:
754
+ return False
755
+
756
+ def _prepare_cached(self, cached_statement):
757
+ self._statement_id = cached_statement.statement_id
758
+
759
+ if self._statementType == StatementType.CALL or self._statementType == StatementType.CALLWITHRESULT:
760
+ if len(self._params._params_list) != len(cached_statement._params._params_list):
761
+ if (
762
+ self._statementType == StatementType.CALL
763
+ and self._has_return_value == 0
764
+ and cached_statement._has_return_value == 1
765
+ and len(self._params._params_list) + 1 == len(cached_statement._params._params_list)
766
+ ):
767
+ self._params._params_list.insert(
768
+ 0, intersystems_iris.dbapi._Parameter._Parameter(None, ParameterMode.OUTPUT, "c")
769
+ )
770
+ else:
771
+ if len(self._params._params_list) == 0 or len(self._params._params_list) == 1:
772
+ self._params._clear()
773
+ else:
774
+ return False
775
+
776
+ if cached_statement._statementType == StatementType.QUERY:
777
+ self._statementType = StatementType.PREPARED_CALL_QUERY
778
+ else:
779
+ if cached_statement._statementType == StatementType.UPDATE:
780
+ self._statementType = StatementType.PREPARED_CALL_UPDATE
781
+ else:
782
+ self._statementType = cached_statement._statementType
783
+
784
+ self._has_return_value = cached_statement._has_return_value
785
+ self._multiple_result_sets = cached_statement.multiple_result_sets
786
+ self._mrs_done = False
787
+
788
+ if not self._multiple_result_sets and (
789
+ self._statementType
790
+ in [StatementType.QUERY, StatementType.PREPARED_CALL_QUERY, StatementType.DIRECT_CALL_QUERY]
791
+ ):
792
+ self._columns = []
793
+ for column in cached_statement.columns:
794
+ self._columns.append(column.Clone())
795
+
796
+ if self.statementFeatureOption & Feature.optionFastSelect == Feature.optionFastSelect:
797
+ self._rsrow = _ResultSetRow(self._connection, self._columns, cached_statement.maxRowItemCount)
798
+ else:
799
+ self._rsrow = _ResultSetRow(self._connection, self._columns, 0)
800
+
801
+ else:
802
+ if self._statementType == StatementType.QUERY:
803
+ self._columns = []
804
+ for column in cached_statement.columns:
805
+ self._columns.append(column.Clone())
806
+
807
+ if self.statementFeatureOption & Feature.optionFastSelect == Feature.optionFastSelect:
808
+ self._rsrow = _ResultSetRow(self._connection, self._columns, cached_statement.maxRowItemCount)
809
+ else:
810
+ self._rsrow = _ResultSetRow(self._connection, self._columns, 0)
811
+
812
+ if hasattr(cached_statement, "statementFeatureOption"):
813
+ self.statementFeatureOption = cached_statement.statementFeatureOption
814
+ if hasattr(cached_statement, "maxRowItemCount"):
815
+ self.maxRowItemCount = cached_statement.maxRowItemCount
816
+ if hasattr(cached_statement, "_params"):
817
+ self._params._update_param_info(cached_statement._params)
818
+
819
+ def _prepare_new(self):
820
+ # send PP message
821
+ with self._connection._lock:
822
+ # message header
823
+ self._statement_id = self._connection._get_new_statement_id()
824
+ self._out_message.wire._write_header(_Message.PREPARE)
825
+ intersystems_iris._MessageHeader._MessageHeader._set_statement_id(
826
+ self._out_message.wire.buffer, self._statement_id
827
+ )
828
+
829
+ # message body
830
+ self._out_message.wire._set(1) # number of statement chunks
831
+ self._out_message.wire._set(self._parsed_statement) # statement itself
832
+ self._out_message.wire._set_raw_bytes(self._paramInfo.getBuffer()) # paramInfo (from _PreParser)
833
+
834
+ # send message
835
+ sequence_number = self._connection._get_new_sequence_number()
836
+ self._out_message._send(sequence_number)
837
+
838
+ # retrieve response
839
+ self._in_message._read_message_sql(sequence_number, self._statement_id, 0, [0])
840
+ sqlcode = self._in_message.wire.header._get_function_code()
841
+ if sqlcode not in [0, 100]:
842
+ raise DatabaseError(self._get_error_info(sqlcode))
843
+
844
+ # process metadata
845
+ try:
846
+ if self._connection._isFastOption():
847
+ self._check_statement_feature(self._in_message.wire)
848
+ else:
849
+ self.statementFeatureOption = Feature.optionNone
850
+
851
+ if self._statementType == StatementType.QUERY:
852
+ self._get_column_info(self._in_message.wire)
853
+ else:
854
+ self._columns = None
855
+
856
+ addToCache = self._get_parameter_info(self._in_message.wire)
857
+ if addToCache:
858
+ self._cache_prepared_statement()
859
+ except IndexError:
860
+ raise DatabaseError("Server response message terminated prematurely")
861
+ except TypeError:
862
+ raise DatabaseError("Unexpected server response message format")
863
+
864
+ def _get_error_info(self, sqlcode):
865
+ with self._connection._lock:
866
+ self._out_message.wire._write_header(_Message.GET_SERVER_ERROR)
867
+ self._out_message.wire._set(sqlcode)
868
+
869
+ sequence_number = self._connection._get_new_sequence_number()
870
+ self._out_message._send(sequence_number)
871
+
872
+ self._in_message._read_message_sql(sequence_number)
873
+ return self._in_message.wire._get()
874
+
875
+ def _check_statement_feature(self, wire):
876
+ self.statementFeatureOption = wire._get()
877
+ if (
878
+ self.statementFeatureOption == Feature.optionFastSelect
879
+ or self.statementFeatureOption == Feature.optionFastInsert
880
+ ):
881
+ self.maxRowItemCount = wire._get()
882
+ else:
883
+ self.maxRowItemCount = 0
884
+
885
+ def _get_column_info(self, wire):
886
+ self._columns = []
887
+ count = wire._get()
888
+ for i in range(count):
889
+ name = wire._get()
890
+ type = wire._get()
891
+ precision = wire._get()
892
+ scale = wire._get()
893
+ nullable = wire._get()
894
+ label = wire._get()
895
+ tableName = wire._get()
896
+ schema = wire._get()
897
+ catalog = wire._get()
898
+ if catalog == 0:
899
+ catalog = None
900
+ additionalData = wire._get().encode()
901
+ slotPosition = (
902
+ wire._get()
903
+ if self.statementFeatureOption & Feature.optionFastSelect == Feature.optionFastSelect
904
+ else i + 1
905
+ )
906
+ self._columns.append(
907
+ intersystems_iris.dbapi._Column._Column(
908
+ name,
909
+ type,
910
+ precision,
911
+ scale,
912
+ nullable,
913
+ label,
914
+ tableName,
915
+ schema,
916
+ catalog,
917
+ additionalData,
918
+ slotPosition,
919
+ )
920
+ )
921
+
922
+ def _get_parameter_info(self, wire):
923
+ count = wire._get()
924
+ if count != len(self._params._params_list):
925
+ raise Exception("Invalid number of parameters")
926
+ self._read_parameter_data(wire, count, False)
927
+
928
+ addToCache = bool(wire._get())
929
+ return addToCache
930
+
931
+ def _read_parameter_data(self, wire, count, is_stored_procedure):
932
+ if count != len(self._params._params_list):
933
+ raise Exception("Invalid number of parameters")
934
+ with self._connection._lock:
935
+ r = 0
936
+ user_param_count = 0
937
+ if self._has_return_value == ServerReturnType.NULL_RETURN_VALUE:
938
+ r += 1
939
+ if self._has_return_value == ServerReturnType.IGNORE_RETURN_VALUE:
940
+ user_param_count -= 1
941
+
942
+ for i in range(count):
943
+ param = self._params._params_list[i + r]
944
+ param.type = wire._get()
945
+ param.precision = wire._get()
946
+ param.scale = wire._get()
947
+ param.nullable = wire._get()
948
+
949
+ if self.statementFeatureOption & Feature.optionFastInsert == Feature.optionFastInsert:
950
+ param.slotPosition = wire._get()
951
+ self.rowOfDefaultValues = wire._get() # needs to be processed
952
+ else:
953
+ param.slotPosition = i + r
954
+
955
+ if is_stored_procedure:
956
+ param.name = wire._get()
957
+ wire._get()
958
+
959
+ if is_stored_procedure:
960
+ self._params._update_names()
961
+
962
+ def _execute_update(self):
963
+ super()._execute_update()
964
+
965
+ if self._parameter_sets == 0 and not self._multiple_result_sets:
966
+ self._rowcount = self._in_message.wire._get()
967
+
968
+ class prepared_statement(intersystems_iris._IRISConnection.CachedSQL):
969
+ def __init__(self, cursor):
970
+ if not isinstance(cursor, Cursor):
971
+ raise TypeError("cursor must be a Cursor")
972
+
973
+ super().__init__(cursor)
974
+ self.statement = cursor._parsed_statement
975
+ self.statement_id = cursor._statement_id
976
+
977
+ if cursor._columns != None:
978
+ self.columns = []
979
+ for column in cursor._columns:
980
+ self.columns.append(column.Clone())
981
+
982
+ if hasattr(cursor, "statementFeatureOption"):
983
+ self.statementFeatureOption = cursor.statementFeatureOption
984
+ if hasattr(cursor, "maxRowItemCount"):
985
+ self.maxRowItemCount = cursor.maxRowItemCount
986
+
987
+ self.multiple_result_sets = cursor._multiple_result_sets
988
+
989
+ def _cache_prepared_statement(self):
990
+ self._connection._cache_prepared_statement(self.prepared_statement(self))
991
+
992
+ def _write_parameters(self):
993
+ sets = self._parameter_sets or 1
994
+
995
+ self._out_message.wire._set(sets) # nParamSets
996
+ self._out_message.wire._set(len(self._params._params_list)) # nParams
997
+ for i in range(sets):
998
+ param_index = 0
999
+ param_counter = i
1000
+ for param in self._params._params_list:
1001
+ mode = param.mode
1002
+ if mode == ParameterMode.REPLACED_LITERAL:
1003
+ self._out_message.wire._set_parameter(param)
1004
+ elif not mode == ParameterMode.INPUT:
1005
+ temp_param = intersystems_iris.dbapi._Parameter._Parameter(param._values[i])
1006
+ self._out_message.wire._set_parameter(temp_param)
1007
+ elif len(self.params) > 0:
1008
+ item = self.params[param_counter]
1009
+ if isinstance(item, list) or isinstance(item, tuple):
1010
+ value = item[param_index]
1011
+ param_index = param_index + 1
1012
+ else:
1013
+ value = item
1014
+ param_counter = param_counter + 1
1015
+ temp_param = intersystems_iris.dbapi._Parameter._Parameter(value)
1016
+ self._out_message.wire._set_parameter(temp_param)
1017
+ else:
1018
+ raise Exception("Missing value")
1019
+
1020
+ def _prepared_query_execute(self):
1021
+ # send PQ message
1022
+ with self._connection._lock:
1023
+ # message header
1024
+ self._out_message.wire._write_header(_Message.PREPARED_QUERY_EXECUTE)
1025
+ intersystems_iris._MessageHeader._MessageHeader._set_statement_id(
1026
+ self._out_message.wire.buffer, self._statement_id
1027
+ )
1028
+
1029
+ # message body
1030
+ self._write_parameters()
1031
+ self._out_message.wire._set(0) # query timeout
1032
+ self._out_message.wire._set(0) # maxRows (0 = all rows)
1033
+
1034
+ # send
1035
+ sequence_number = self._connection._get_new_sequence_number()
1036
+ self._out_message._send(sequence_number)
1037
+
1038
+ # retrieve response
1039
+ self._in_message._read_message_sql(sequence_number, self._statement_id, _InStream.FETCH_DATA, [404, 100])
1040
+ self._sqlcode = self._in_message.wire.header._get_function_code()
1041
+ self._handle_error_504(self._sqlcode)
1042
+ if self._sqlcode == 404:
1043
+ return
1044
+ self._process_sqlcode(self._sqlcode)
1045
+
1046
+ self._current_wire = self._in_message.wire
1047
+ self._result_set = [self._current_wire]
1048
+ self._rs_index = 0
1049
+
1050
+ self._rowcount = -1
1051
+
1052
+ def _send_direct_stored_procedure_request(self):
1053
+ # self._has_return_value = ServerReturnType.NO_RETURN_VALUE
1054
+ # send DS message
1055
+ with self._connection._lock:
1056
+ # message header
1057
+ self._statement_id = self._connection._get_new_statement_id()
1058
+ self._out_message.wire._write_header(_Message.DIRECT_STORED_PROCEDURE)
1059
+ intersystems_iris._MessageHeader._MessageHeader._set_statement_id(
1060
+ self._out_message.wire.buffer, self._statement_id
1061
+ )
1062
+
1063
+ # message body
1064
+ self._out_message.wire._set(self._parsed_statement) # statement itself
1065
+ self._out_message.wire._set(0) # resultSetType != ResultSet.TYPE_SCROLL_INSENSITIVE
1066
+ self._out_message.wire._set(0) # query timeout
1067
+ self._out_message.wire._set(0) # maxRows (0 = all rows)
1068
+ self._write_stored_procedure_parameters()
1069
+
1070
+ # send
1071
+ sequence_number = self._connection._get_new_sequence_number()
1072
+ self._out_message._send(sequence_number)
1073
+
1074
+ try:
1075
+ # retrieve metadata
1076
+ self._in_message._read_message_sql(sequence_number, self._statement_id, 0, [100])
1077
+ sqlcode = self._in_message.wire.header._get_function_code()
1078
+ if sqlcode not in [0, 100]:
1079
+ raise DatabaseError(self._get_error_info(sqlcode))
1080
+
1081
+ self._process_stored_procedure_metadata(self._in_message.wire, True)
1082
+ if self._multiple_result_sets:
1083
+ # todo
1084
+ return
1085
+
1086
+ self._cache_prepared_statement()
1087
+
1088
+ if self._statementType in [
1089
+ StatementType.UPDATE,
1090
+ StatementType.DIRECT_CALL_UPDATE,
1091
+ StatementType.PREPARED_CALL_UPDATE,
1092
+ ]:
1093
+ return False
1094
+
1095
+ except IndexError:
1096
+ raise DatabaseError("Server response message terminated prematurely")
1097
+ except TypeError:
1098
+ raise DatabaseError("Unexpected server response message format")
1099
+
1100
+ def _prepared_stored_procedure_execute(self):
1101
+ # send SU message
1102
+ with self._connection._lock:
1103
+ # message header
1104
+ self._out_message.wire._write_header(_Message.PREPARED_UPDATE_EXECUTE)
1105
+ intersystems_iris._MessageHeader._MessageHeader._set_statement_id(
1106
+ self._out_message.wire.buffer, self._statement_id
1107
+ )
1108
+
1109
+ def _send_direct_query_request(self):
1110
+ # send DQ message
1111
+ with self._connection._lock:
1112
+ # message header
1113
+ self._statement_id = self._connection._get_new_statement_id()
1114
+ self._out_message.wire._write_header(_Message.DIRECT_QUERY)
1115
+ intersystems_iris._MessageHeader._MessageHeader._set_statement_id(
1116
+ self._out_message.wire.buffer, self._statement_id
1117
+ )
1118
+
1119
+ # message body
1120
+ self._out_message.wire._set(1) # number of statement chunks
1121
+ self._out_message.wire._set(self._parsed_statement) # statement itself
1122
+ self._out_message.wire._set_raw_bytes(self._paramInfo.getBuffer()) # paramInfo (from _PreParser)
1123
+ self._write_parameters()
1124
+ self._out_message.wire._set(0) # query timeout
1125
+ self._out_message.wire._set(0) # maxRows (0 = all rows)
1126
+
1127
+ # send
1128
+ sequence_number = self._connection._get_new_sequence_number()
1129
+ self._out_message._send(sequence_number)
1130
+
1131
+ try:
1132
+ # retrieve metadata
1133
+ self._in_message._read_message_sql(sequence_number, self._statement_id, 0, [100])
1134
+ sqlcode = self._in_message.wire.header._get_function_code()
1135
+ self._process_sqlcode(sqlcode)
1136
+
1137
+ if self._connection._isFastOption():
1138
+ self._check_statement_feature(self._in_message.wire)
1139
+ else:
1140
+ self.statementFeatureOption = Feature.optionNone
1141
+ self._get_column_info(self._in_message.wire)
1142
+ self._get_parameter_info(self._in_message.wire)
1143
+ self._cache_prepared_statement()
1144
+
1145
+ # retrieve data
1146
+ self._in_message._read_message_sql(sequence_number, self._statement_id, _InStream.FETCH_DATA, [100])
1147
+ self._sqlcode = self._in_message.wire.header._get_function_code()
1148
+ if self._sqlcode not in [0, 100]:
1149
+ raise DatabaseError(self._get_error_info(self._sqlcode))
1150
+
1151
+ except IndexError:
1152
+ raise DatabaseError("Server response message terminated prematurely")
1153
+ except TypeError:
1154
+ raise DatabaseError("Unexpected server response message format")
1155
+
1156
+ self._current_wire = self._in_message.wire
1157
+ self._result_set = [self._current_wire]
1158
+ self._rs_index = 0
1159
+
1160
+ self._rowcount = -1
1161
+ if self.statementFeatureOption & Feature.optionFastSelect == Feature.optionFastSelect:
1162
+ self._rsrow = _ResultSetRow(self._connection, self._columns, self.maxRowItemCount)
1163
+ else:
1164
+ self._rsrow = _ResultSetRow(self._connection, self._columns, 0)
1165
+
1166
+ def _update_streams(self):
1167
+ sets = self._parameter_sets or 1
1168
+ self.params = list(self.params).copy()
1169
+ param_types = [param.type for param in self._params._params_list]
1170
+
1171
+ for i in range(sets):
1172
+ params = self._params.collect(i)
1173
+ for pi, param in enumerate(params):
1174
+ if param_types[pi] in (SQLType.LONGVARBINARY, SQLType.LONGVARCHAR) and param is not None:
1175
+ stream_oref = self._send_stream(param_types[pi], param)
1176
+ if isinstance(self.params[i], tuple):
1177
+ self.params[i] = list(self.params[i])
1178
+ if isinstance(self.params[i], list):
1179
+ self.params[i][pi] = stream_oref
1180
+ else:
1181
+ self.params[pi] = stream_oref
1182
+
1183
+ def _send_stream(self, param_type, value):
1184
+ if not isinstance(value, str) and not isinstance(value, bytes):
1185
+ raise Exception(f"Invalid value type for stream, got {type(value).__name__}, expected str or bytes")
1186
+ stream_oref = None
1187
+ offset = 0
1188
+ full_size = len(value)
1189
+ with self._connection._lock:
1190
+ while True:
1191
+ size = full_size
1192
+ if size == 0:
1193
+ break
1194
+ size = 4096 if size > 4096 else size
1195
+ chunk = value[offset:offset + size]
1196
+ if not isinstance(chunk, bytes):
1197
+ chunk = bytes(chunk, "utf-8")
1198
+ offset += size
1199
+ full_size -= size
1200
+
1201
+ # message header
1202
+ code = _Message.STORE_BINARY_STREAM if param_type == SQLType.LONGVARBINARY else _Message.STORE_CHARACTER_STREAM
1203
+ self._out_message.wire._write_header(code)
1204
+ intersystems_iris._MessageHeader._MessageHeader._set_statement_id(
1205
+ self._out_message.wire.buffer, self._statement_id
1206
+ )
1207
+ # message body
1208
+ self._out_message.wire._set(stream_oref or "0")
1209
+ self._out_message.wire._set_raw_bytes(struct.pack("<i", size))
1210
+ self._out_message.wire._set_raw_bytes(chunk)
1211
+
1212
+ # send
1213
+ sequence_number = self._connection._get_new_sequence_number()
1214
+ self._out_message._send(sequence_number)
1215
+
1216
+ self._in_message._read_message_sql(sequence_number, self._statement_id, 0)
1217
+ stream_oref = self._sqlcode = self._in_message.wire._get()
1218
+
1219
+ return stream_oref
1220
+
1221
+ def _prepared_update_execute(self):
1222
+ self._update_streams()
1223
+
1224
+ # send PU message
1225
+ with self._connection._lock:
1226
+ # message header
1227
+ self._out_message.wire._write_header(_Message.PREPARED_UPDATE_EXECUTE)
1228
+ intersystems_iris._MessageHeader._MessageHeader._set_statement_id(
1229
+ self._out_message.wire.buffer, self._statement_id
1230
+ )
1231
+
1232
+ # message body
1233
+ self._lastrowid = None
1234
+ self._out_message.wire._set(-1) # autoGeneratedKeyColumn
1235
+ self._out_message.wire._set(0) # statement timeout always 0 for non-queries
1236
+ self._write_parameters()
1237
+
1238
+ # send
1239
+ sequence_number = self._connection._get_new_sequence_number()
1240
+ self._out_message._send(sequence_number)
1241
+
1242
+ # retrieve response
1243
+ self._in_message._read_message_sql(sequence_number, self._statement_id, 0, [404, 100])
1244
+ self._sqlcode = self._in_message.wire.header._get_function_code()
1245
+ if self._sqlcode == 404:
1246
+ self._update404()
1247
+ return
1248
+ self._process_sqlcode(self._sqlcode)
1249
+
1250
+ def _send_direct_update_request(self):
1251
+ # send DU message
1252
+ with self._connection._lock:
1253
+ self._statement_id = self._connection._get_new_statement_id()
1254
+ # message header
1255
+ self._out_message.wire._write_header(_Message.DIRECT_UPDATE)
1256
+ intersystems_iris._MessageHeader._MessageHeader._set_statement_id(
1257
+ self._out_message.wire.buffer, self._statement_id
1258
+ )
1259
+
1260
+ # message body
1261
+ self._lastrowid = None
1262
+ self._out_message.wire._set(1) # number of statement chunks
1263
+ self._out_message.wire._set(self._parsed_statement) # statement itself
1264
+ self._out_message.wire._set_raw_bytes(self._paramInfo.getBuffer()) # paramInfo (from _PreParser)
1265
+ self._out_message.wire._set(-1) # autoGeneratedKeyColumn
1266
+ self._out_message.wire._set(0) # statement timeout always 0 for non-queries
1267
+ self._write_parameters()
1268
+
1269
+ # send
1270
+ sequence_number = self._connection._get_new_sequence_number()
1271
+ self._out_message._send(sequence_number)
1272
+
1273
+ try:
1274
+ # retrieve response
1275
+ self._in_message._read_message_sql(sequence_number, self._statement_id, 0, [100])
1276
+ self._sqlcode = self._in_message.wire.header._get_function_code()
1277
+ self._process_sqlcode(self._sqlcode)
1278
+
1279
+ addToCache = self._get_parameter_info(self._in_message.wire)
1280
+
1281
+ notDDL = bool(
1282
+ self._statementType != StatementType.DDL_ALTER_DROP
1283
+ and self._statementType != StatementType.DDL_OTHER
1284
+ )
1285
+ if notDDL and addToCache:
1286
+ self._cache_prepared_statement()
1287
+
1288
+ except IndexError:
1289
+ raise DatabaseError("Server response message terminated prematurely")
1290
+ except TypeError:
1291
+ raise DatabaseError("Unexpected server response message format")
1292
+
1293
+ def _prepare_stored_procedure(self):
1294
+ with self._connection._lock:
1295
+ # message header
1296
+ self._statement_id = self._connection._get_new_statement_id()
1297
+ self._out_message.wire._write_header(_Message.PREPARE_STORED_PROCEDURE)
1298
+ intersystems_iris._MessageHeader._MessageHeader._set_statement_id(
1299
+ self._out_message.wire.buffer, self._statement_id
1300
+ )
1301
+
1302
+ # message body
1303
+ # self._out_message.wire._set(1) # number of statement chunks
1304
+ self._out_message.wire._set(self._parsed_statement) # statement itself
1305
+
1306
+ # send message
1307
+ sequence_number = self._connection._get_new_sequence_number()
1308
+ self._out_message._send(sequence_number)
1309
+
1310
+ # retrieve response
1311
+ self._in_message._read_message_sql(sequence_number, self._statement_id, 0, [0])
1312
+ sqlcode = self._in_message.wire.header._get_function_code()
1313
+ self._process_sqlcode(sqlcode)
1314
+
1315
+ self._process_stored_procedure_metadata(self._in_message.wire, False)
1316
+ if self._multiple_result_sets:
1317
+ return
1318
+ self._cache_prepared_statement()
1319
+ return
1320
+
1321
+ def _process_stored_procedure_metadata(self, wire, direct_execute):
1322
+ ret_stmt_type = wire._get()
1323
+ if ret_stmt_type < 0:
1324
+ if ret_stmt_type == -1:
1325
+ self._multiple_result_sets_metadata(wire, False, direct_execute)
1326
+ else:
1327
+ self._multiple_result_sets_metadata(wire, True, direct_execute)
1328
+ return
1329
+ if ret_stmt_type % 2 == StatementType.QUERY.value:
1330
+ self._get_column_info(wire)
1331
+ if not self._stored_procedure_parameter_info(wire, ret_stmt_type > 1, direct_execute):
1332
+ if direct_execute and ret_stmt_type % 2 == StatementType.QUERY.value:
1333
+ # result set type check
1334
+ self._in_message.wire._move_to_end()
1335
+ if direct_execute:
1336
+ raise Exception("Parameter list mismatch")
1337
+ if direct_execute and self._parameter_list_mismatch_exception:
1338
+ raise Exception("Parameter list mismatch")
1339
+ if self._cursor_type == CursorType.CALLABLE:
1340
+ self._statementType = StatementType(ret_stmt_type % 2)
1341
+ return
1342
+ if self._cursor_type == CursorType.PREPARED:
1343
+ if ret_stmt_type % 2 == StatementType.QUERY.value:
1344
+ self._statementType = StatementType.PREPARED_CALL_QUERY
1345
+ else:
1346
+ self._statementType = StatementType.PREPARED_CALL_UPDATE
1347
+ return
1348
+ if ret_stmt_type % 2 == StatementType.QUERY.value:
1349
+ self._statementType = StatementType.DIRECT_CALL_QUERY
1350
+ else:
1351
+ self._statementType = StatementType.DIRECT_CALL_UPDATE
1352
+ return
1353
+
1354
+ def _multiple_result_sets_metadata(self, wire, server_has_return, direct_execute):
1355
+ self._multiple_result_sets = True
1356
+ self._mrs_done = False
1357
+ if not self._stored_procedure_parameter_info(wire, server_has_return, direct_execute):
1358
+ raise Exception("Parameter list mismatch")
1359
+
1360
+ if self._cursor_type == CursorType.CALLABLE:
1361
+ self._statementType = StatementType.QUERY
1362
+ elif self._cursor_type == CursorType.PREPARED:
1363
+ self._statementType = StatementType.PREPARED_CALL_QUERY
1364
+ else:
1365
+ self._statementType = StatementType.DIRECT_CALL_QUERY
1366
+
1367
+ def _stored_procedure_parameter_info(self, wire, server_has_return, direct_execute):
1368
+ count = wire._get()
1369
+ size = len(self._params._params_list)
1370
+ if self._cursor_type != CursorType.CALLABLE and self._has_return_value == 1:
1371
+ wire._move_to_end()
1372
+ return False
1373
+ if self._exec_params != None:
1374
+ while size < count:
1375
+ self._params._params_list.insert(0, intersystems_iris.dbapi._Parameter._Parameter())
1376
+ size = size + 1
1377
+ if server_has_return:
1378
+ self._has_return_value = ServerReturnType.IGNORE_RETURN_VALUE
1379
+ elif size == count:
1380
+ if server_has_return and self._has_return_value == ServerReturnType.HAS_RETURN_VALUE:
1381
+ self._has_return_value = ServerReturnType.HAS_RETURN_VALUE
1382
+ elif not server_has_return and self._has_return_value == ServerReturnType.NO_RETURN_VALUE:
1383
+ self._has_return_value = ServerReturnType.NO_RETURN_VALUE
1384
+ elif (
1385
+ size == 1
1386
+ and count == 1
1387
+ and (
1388
+ self._params._params_list[0].mode == ParameterMode.DEFAULT_PARAMETER
1389
+ and self._has_return_value == ServerReturnType.NO_RETURN_VALUE
1390
+ and server_has_return
1391
+ )
1392
+ or (
1393
+ self._params._params_list[0].mode == ParameterMode.UNKNOWN
1394
+ and self._has_return_value == ServerReturnType.IGNORE_RETURN_VALUE
1395
+ and server_has_return
1396
+ )
1397
+ ):
1398
+ self._params._params_list.pop(0)
1399
+ self._params._params_list.insert(0, intersystems_iris.dbapi._Parameter._Parameter())
1400
+ self._has_return_value = ServerReturnType.IGNORE_RETURN_VALUE
1401
+ else:
1402
+ wire._move_to_end()
1403
+ return False
1404
+ elif size == count + 1:
1405
+ if not server_has_return and self._has_return_value == ServerReturnType.HAS_RETURN_VALUE:
1406
+ self._has_return_value = ServerReturnType.NULL_RETURN_VALUE
1407
+ elif (size == 2 and count == 1) or (size == 1 and count == 0):
1408
+ if self._params._params_list[-1].mode == ParameterMode.DEFAULT_PARAMETER:
1409
+ if server_has_return and self._has_return_value == ServerReturnType.HAS_RETURN_VALUE:
1410
+ self._params._params_list.pop(-1)
1411
+ self._has_return_value = ServerReturnType.HAS_RETURN_VALUE
1412
+ elif not server_has_return and self._has_return_value == ServerReturnType.NO_RETURN_VALUE:
1413
+ self._params._params_list.pop(-1)
1414
+ self._has_return_value = ServerReturnType.NO_RETURN_VALUE
1415
+ else:
1416
+ wire._move_to_end()
1417
+ return False
1418
+ else:
1419
+ wire._move_to_end()
1420
+ return False
1421
+ else:
1422
+ wire._move_to_end()
1423
+ return False
1424
+ elif size == count - 1:
1425
+ if server_has_return and self._has_return_value == ServerReturnType.NO_RETURN_VALUE:
1426
+ self._params._params_list.insert(0, intersystems_iris.dbapi._Parameter._Parameter())
1427
+ self._has_return_value = ServerReturnType.IGNORE_RETURN_VALUE
1428
+ else:
1429
+ self._params._params_list.append(
1430
+ intersystems_iris.dbapi._Parameter._Parameter(ParameterMode.DEFAULT_PARAMETER, "c")
1431
+ )
1432
+ else:
1433
+ self._parameter_list_mismatch_exception = True
1434
+ if server_has_return and self._has_return_value == ServerReturnType.NO_RETURN_VALUE:
1435
+ self._has_return_value = ServerReturnType.IGNORE_RETURN_VALUE
1436
+ param = intersystems_iris.dbapi._Parameter._Parameter()
1437
+ param.mode = ParameterMode.OUTPUT
1438
+ self._params._params_list.insert(0, param)
1439
+ size = size + 1
1440
+ while size < count:
1441
+ self._params._params_list.append(
1442
+ intersystems_iris.dbapi._Parameter._Parameter(ParameterMode.DEFAULT_PARAMETER, "c")
1443
+ )
1444
+ size = size + 1
1445
+ self._read_parameter_data(wire, count, True)
1446
+ return True
1447
+
1448
+ def _bind_exec_params(self):
1449
+ for param in self._params._params_list:
1450
+ exec_param = self._get_exec_param_by_name(param.name)
1451
+ if exec_param != None:
1452
+ if exec_param.mode == ParameterMode.UNKNOWN:
1453
+ continue
1454
+ param.mode = exec_param.mode
1455
+ if exec_param.mode != ParameterMode.OUTPUT:
1456
+ if isinstance(exec_param.value, list):
1457
+ self._params._array_bound = True
1458
+ param._bind(exec_param.value, self._parameter_sets)
1459
+ else:
1460
+ param.__bound = exec_param.bound
1461
+ if exec_param.scale != -1:
1462
+ param.scale = exec_param.scale
1463
+ else:
1464
+ if param.mode == ParameterMode.UNKNOWN:
1465
+ param.mode = ParameterMode.DEFAULT_PARAMETER
1466
+
1467
+ def _get_exec_param_by_name(self, name):
1468
+ self._exec_params._has_bound_by_param_name = True
1469
+ return self._exec_params._params_list[self._exec_params._param_names.get(name.upper())]
1470
+
1471
+ def _stored_procedure_update(self):
1472
+ with self._connection._lock:
1473
+ # message header
1474
+ self._out_message.wire._write_header(_Message.STORED_PROCEDURE_UPDATE_EXECUTE)
1475
+ intersystems_iris._MessageHeader._MessageHeader._set_statement_id(
1476
+ self._out_message.wire.buffer, self._statement_id
1477
+ )
1478
+
1479
+ # message body
1480
+ self._out_message.wire._set(0) # isStatic should always be 0 for non-queries
1481
+ self._out_message.wire._set(0) # query timeout
1482
+ self._out_message.wire._set(0) # maxRows (0 = all rows)
1483
+ self._write_stored_procedure_parameters()
1484
+
1485
+ # send
1486
+ sequence_number = self._connection._get_new_sequence_number()
1487
+ self._out_message._send(sequence_number)
1488
+
1489
+ # retrieve response
1490
+ self._in_message._read_message_sql(sequence_number, self._statement_id, 0, [404, 100])
1491
+ self._sqlcode = self._in_message.wire.header._get_function_code()
1492
+ self._process_sqlcode(self._sqlcode)
1493
+ if self._sqlcode == 404:
1494
+ self._update404(404)
1495
+ else:
1496
+ self._get_output_parameters(self._in_message.wire)
1497
+ return
1498
+
1499
+ def _stored_procedure_query(self):
1500
+ if self._multiple_result_sets:
1501
+ self._execute_multiple_result_sets(False)
1502
+ return
1503
+ with self._connection._lock:
1504
+ # message header
1505
+ self._out_message.wire._write_header(_Message.STORED_PROCEDURE_QUERY_EXECUTE)
1506
+ intersystems_iris._MessageHeader._MessageHeader._set_statement_id(
1507
+ self._out_message.wire.buffer, self._statement_id
1508
+ )
1509
+
1510
+ # message body
1511
+ self._out_message.wire._set(0) # ResultSet.TYPE_SCROLL_INSENSITIVE
1512
+ self._out_message.wire._set(0) # query timeout
1513
+ self._out_message.wire._set(0) # maxRows (0 = all rows)
1514
+ self._write_stored_procedure_parameters()
1515
+
1516
+ # send
1517
+ sequence_number = self._connection._get_new_sequence_number()
1518
+ self._out_message._send(sequence_number)
1519
+
1520
+ # retrieve response
1521
+ self._in_message._read_message_sql(sequence_number, self._statement_id, 0, [404, 100])
1522
+ self._sqlcode = self._in_message.wire.header._get_function_code()
1523
+ if self._sqlcode == 404:
1524
+ self._handle_error_504(404)
1525
+ return
1526
+ elif self._sqlcode == 100:
1527
+ self._handle_error_100(100)
1528
+ return
1529
+ self._get_output_parameters(self._in_message.wire)
1530
+ self._in_message._read_message_sql(sequence_number, self._statement_id, _InStream.FETCH_DATA, [100])
1531
+ self._sqlcode = self._in_message.wire.header._get_function_code()
1532
+ self._process_sqlcode(self._sqlcode)
1533
+ if self._sqlcode == 100:
1534
+ self._handle_error_100(100)
1535
+
1536
+ self._current_wire = self._in_message.wire
1537
+ self._result_set = [self._current_wire]
1538
+ self._rs_index = 0
1539
+
1540
+ def _execute_multiple_result_sets(self, validate):
1541
+ self._fetch_done = False
1542
+ if validate:
1543
+ self._validate_parameters()
1544
+ with self._connection._lock:
1545
+ self._out_message.wire._write_header(_Message.EXECUTE_MULTIPLE_RESULT_SETS)
1546
+ self._out_message.wire._set(0) # resultSetType != ResultSet.TYPE_SCROLL_INSENSITIVE
1547
+ self._out_message.wire._set(0) # query timeout
1548
+ self._write_stored_procedure_parameters()
1549
+
1550
+ # send
1551
+ sequence_number = self._connection._get_new_sequence_number()
1552
+ self._out_message._send(sequence_number)
1553
+
1554
+ if self.statementFeatureOption & Feature.optionFastSelect == Feature.optionFastSelect:
1555
+ self._rsrow = _ResultSetRow(self._connection, self._columns, self.maxRowItemCount)
1556
+ else:
1557
+ self._rsrow = _ResultSetRow(self._connection, self._columns, 0)
1558
+
1559
+ self._in_message._read_message_sql(sequence_number, self._statement_id, _InStream.FETCH_DATA, [100])
1560
+ self._sqlcode = self._in_message.wire.header._get_function_code()
1561
+ if self._sqlcode == 100:
1562
+ self._fetch_done = True
1563
+ self._get_output_parameters(self._in_message.wire)
1564
+
1565
+ self._current_wire = self._in_message.wire
1566
+ self._result_set = [self._current_wire]
1567
+ self._rs_index = 0
1568
+
1569
+ results = self._in_message.wire._get()
1570
+ if results >= 0:
1571
+ self._update_cnt = results
1572
+ return False
1573
+ elif results == -1:
1574
+ return True
1575
+ elif results == -2:
1576
+ self._update_cnt = -1
1577
+ self._mrs_done = True
1578
+ # self._rsrow._is_after_last = True
1579
+ return False
1580
+ else:
1581
+ raise Exception("Invalid result type value")
1582
+
1583
+ def _write_stored_procedure_parameters(self):
1584
+ i = 0
1585
+ if self._parameter_sets != 0:
1586
+ self._out_message.wire._set(self._parameter_sets)
1587
+ self._out_message.wire._set(len(self._params._params_list))
1588
+ for j in range(self._parameter_sets):
1589
+ for i, param in enumerate(self._params._params_list):
1590
+ self._out_message.wire._set_parameter(param._values[j])
1591
+ return
1592
+ self._out_message.wire._set(1)
1593
+ if self._has_return_value != ServerReturnType.NO_RETURN_VALUE:
1594
+ i = 1
1595
+ self._out_message.wire._set(len(self._params._params_list) - i)
1596
+ param_index = 0
1597
+ param_counter = 0
1598
+ for param in self._params._params_list:
1599
+ if i == 1:
1600
+ i = 0
1601
+ continue
1602
+ if param.mode == ParameterMode.OUTPUT or param.mode == ParameterMode.DEFAULT_PARAMETER:
1603
+ self._out_message.wire._set_undefined()
1604
+ elif not (param.mode == ParameterMode.INPUT or param.mode == ParameterMode.INPUT_OUTPUT):
1605
+ self._out_message.wire._set_parameter_type(param.type, param.value)
1606
+ elif len(self.params) > 0:
1607
+ item = self.params[param_counter]
1608
+ if isinstance(item, list) or isinstance(item, tuple):
1609
+ value = item[param_index]
1610
+ param_index = param_index + 1
1611
+ else:
1612
+ value = item
1613
+ param_counter = param_counter + 1
1614
+ self._out_message.wire._set_parameter_type(param.type, value)
1615
+ else:
1616
+ raise Exception("Missing value")
1617
+
1618
+ def _get_output_parameters(self, wire):
1619
+ beg = wire._get_offset()
1620
+ i = 0
1621
+ if self._has_return_value == ServerReturnType.NULL_RETURN_VALUE:
1622
+ i += 1
1623
+ for param in self._params._params_list:
1624
+ if i == 1:
1625
+ i = 0
1626
+ continue
1627
+ if (
1628
+ param.mode == ParameterMode.INPUT_OUTPUT
1629
+ or param.mode == ParameterMode.OUTPUT
1630
+ or param.mode == ParameterMode.RETURN_VALUE
1631
+ ):
1632
+ wire._next_unless_undefined()
1633
+ else:
1634
+ wire._next()
1635
+ if self._has_return_value == ServerReturnType.NULL_RETURN_VALUE:
1636
+ self._output_parameter_list = wire._get_output_parameter_list(beg, True)
1637
+ self._has_return_value = ServerReturnType.HAS_RETURN_VALUE
1638
+ else:
1639
+ self._output_parameter_list = wire._get_output_parameter_list(beg, False)
1640
+ if self._statementType not in [StatementType.DIRECT_CALL_UPDATE]:
1641
+ self._params._prep_list_index(
1642
+ False, self._output_parameter_list
1643
+ ) # fast select not supported for stored procedures
1644
+ return
1645
+
1646
+ def _handle_error_504(self, error):
1647
+ if error == 404:
1648
+ self._query404()
1649
+ return
1650
+ self._handle_error_100(error)
1651
+
1652
+ def _handle_error_100(self, error):
1653
+ if error == 100:
1654
+ self._fetch_done = True
1655
+ pass
1656
+ else:
1657
+ pass
1658
+
1659
+ def stored_results(self):
1660
+ if self._closed:
1661
+ raise InterfaceError("Cursor is closed")
1662
+ if self._statementType not in [
1663
+ StatementType.QUERY,
1664
+ StatementType.DIRECT_CALL_QUERY,
1665
+ StatementType.PREPARED_CALL_QUERY,
1666
+ ]:
1667
+ return None
1668
+ # getResultSet()
1669
+ if self._multiple_result_sets:
1670
+ if self._rsrow == None and self._rowcount == -1:
1671
+ return None
1672
+ if self._mrs_done:
1673
+ return None
1674
+ self._get_column_info(self._in_message.wire)
1675
+ self.nextset()
1676
+ return iter(self._stored_results)
1677
+
1678
+ def nextset(self):
1679
+ if len(self._stored_results) == 0:
1680
+ # getResultSet()
1681
+ if self.statementFeatureOption & Feature.optionFastSelect == Feature.optionFastSelect:
1682
+ self._rsrow = _ResultSetRow(self._connection, self._columns, self.maxRowItemCount)
1683
+ else:
1684
+ self._rsrow = _ResultSetRow(self._connection, self._columns, 0)
1685
+ self._rsrow.indexRow(self._in_message.wire.list_item)
1686
+ self._stored_results.append(self._rsrow._offsets)
1687
+ return True
1688
+ else:
1689
+ # getMoreResults()
1690
+ if self._closed:
1691
+ raise InterfaceError("Cursor is closed")
1692
+ if self._connection == None or self._connection.isClosed():
1693
+ raise InterfaceError("Connection not open")
1694
+ if (
1695
+ self._mrs_done
1696
+ or not self._multiple_result_sets
1697
+ or (
1698
+ self._statementType != StatementType.PREPARED_CALL_QUERY
1699
+ and self._statementType != StatementType.DIRECT_CALL_QUERY
1700
+ and self._statementType != StatementType.CALL
1701
+ and self._statementType != StatementType.CALLWITHRESULT
1702
+ and not (self._statementType == StatementType.QUERY and self._cursor_type == CursorType.CALLABLE)
1703
+ )
1704
+ ):
1705
+ return False
1706
+ with self._connection._lock:
1707
+ self._out_message.wire._write_header(_Message.GET_MORE_RESULTS)
1708
+ self._out_message.wire._set(1) # current = CLOSE_CURRENT_RESULT
1709
+
1710
+ # send
1711
+ sequence_number = self._connection._get_new_sequence_number()
1712
+ self._out_message._send(sequence_number)
1713
+
1714
+ self._in_message._read_message_sql(sequence_number, self._statement_id, 0, [100])
1715
+ sqlcode = self._in_message.wire.header._get_function_code()
1716
+
1717
+ self._current_wire = self._in_message.wire
1718
+ self._result_set = [self._current_wire]
1719
+ self._rs_index = 0
1720
+
1721
+ results = self._in_message.wire._get()
1722
+ if results >= 0:
1723
+ self._update_cnt = results
1724
+ return False
1725
+ elif results == -1:
1726
+ self._rsrow = None
1727
+ self._get_column_info(self._in_message.wire)
1728
+ if self.statementFeatureOption & Feature.optionFastSelect == Feature.optionFastSelect:
1729
+ self._rsrow = _ResultSetRow(self._connection, self._columns, self.maxRowItemCount)
1730
+ else:
1731
+ self._rsrow = _ResultSetRow(self._connection, self._columns, 0)
1732
+ self._rsrow.indexRow(self._in_message.wire.list_item)
1733
+ self._stored_results.append(self._rsrow._offsets)
1734
+ if sqlcode == 100:
1735
+ self._fetch_done = True
1736
+ else:
1737
+ self._fetch_done = False
1738
+ return True
1739
+ elif results == -2:
1740
+ self._update_cnt = -1
1741
+ self._mrs_done = True
1742
+ return False
1743
+ else:
1744
+ raise Exception("Invalid result type value")
1745
+ return
1746
+
1747
+ def _get_result_set(self, oref):
1748
+ if oref == None:
1749
+ return None
1750
+
1751
+ with self._connection._lock:
1752
+ self._out_message.wire._write_header(_Message.GET_RESULT_SET_OBJECT)
1753
+ self._out_message.wire._set(oref)
1754
+ self._out_message.wire._set(0) # IRISResultSet.GET_RESULT_SET_OBJECT_INIT
1755
+
1756
+ # send
1757
+ sequence_number = self._connection._get_new_sequence_number()
1758
+ self._out_message._send(sequence_number)
1759
+
1760
+ # retrieve response
1761
+ error = self._in_message._read_message_sql(sequence_number, self._statement_id, 0, [100])
1762
+ if error == 100:
1763
+ self._fetch_done = True
1764
+ self._get_column_info(self._in_message.wire)
1765
+ return
1766
+
1767
+ def callproc(self, procname, *params):
1768
+ if self._closed:
1769
+ raise InterfaceError("Cursor is closed")
1770
+ if self._connection == None or self._connection.isClosed():
1771
+ raise InterfaceError("Connection not open")
1772
+
1773
+ self.statement = procname
1774
+ if len(params) == 1 and (isinstance(params[0], tuple) or isinstance(params[0], list)):
1775
+ self.params = self.params[0]
1776
+ else:
1777
+ self.params = params
1778
+
1779
+ self._params.set_input_params(self.params)
1780
+
1781
+ self._cursor_type = CursorType.CALLABLE
1782
+ self._cleanup()
1783
+ self._preparse()
1784
+ self._stored_results = []
1785
+
1786
+ if not self._get_cached_info():
1787
+ self._prepare_stored_procedure()
1788
+
1789
+ # execute() in IrisPreparedStatement
1790
+ if self._multiple_result_sets:
1791
+ return self._execute_multiple_result_sets(True)
1792
+ if self._statementType == StatementType.QUERY or self._statementType == StatementType.PREPARED_CALL_QUERY:
1793
+ self._execute_query()
1794
+ self._rowcount = -1
1795
+ if self._fetch_done and self._in_message.wire.header._get_message_length() == 0:
1796
+ return
1797
+ return self._process_return_values()
1798
+ self._execute_update()
1799
+ if self._parameter_sets == 0 and not self._multiple_result_sets:
1800
+ self._rowcount = self._in_message.wire._get()
1801
+
1802
+ return self._process_return_values()
1803
+
1804
+ def _process_return_values(self):
1805
+ return_args = []
1806
+ for i, param in enumerate(self._params._params_list):
1807
+ if param.mode in [ParameterMode.RETURN_VALUE, ParameterMode.OUTPUT, ParameterMode.INPUT]:
1808
+ offset = self._params._get_user_list_offset(i + 1)
1809
+ val = self._output_parameter_list._get_at_offset(offset)
1810
+ if param.type == -51: # RESULT_SET_TYPE
1811
+ self._get_result_set(val)
1812
+ self.nextset()
1813
+ return_args.append(self._stored_results[0])
1814
+ else:
1815
+ if val == "\x01": # Either represents the number 1 or a null/None value
1816
+ # maybe move this to _grab_ascii_string in DBList?
1817
+ off = self._output_parameter_list.list_item.data_offset
1818
+ buf = self._output_parameter_list.list_item.buffer
1819
+ if buf[off] == 1 and buf[off - 1] == 1 and buf[off - 2] == 3:
1820
+ return_args.append(None)
1821
+ else:
1822
+ return_args.append(val)
1823
+ if len(return_args) > 0:
1824
+ if any(i != None for i in return_args):
1825
+ return return_args
1826
+ else:
1827
+ return
1828
+ else:
1829
+ return
1830
+
1831
+ @property
1832
+ def lastrowid(self):
1833
+ if self._lastrowid is not None:
1834
+ return self._lastrowid
1835
+
1836
+ if self._closed:
1837
+ return None
1838
+ if self._connection == None or self._connection.isClosed():
1839
+ return None
1840
+
1841
+ if self._statementType is not StatementType.UPDATE or self._rowcount < 1:
1842
+ return None
1843
+
1844
+ if self._rowcount > 1:
1845
+ with self._connection.cursor() as cursor:
1846
+ cursor.execute("SELECT LAST_IDENTITY()")
1847
+ self._lastrowid = cursor.fetchone()[0]
1848
+ return self._lastrowid
1849
+
1850
+ # In multiple rows inserted it returns the first inserted value, not the last one
1851
+ with self._connection._lock:
1852
+ self._out_message.wire._write_header(_Message.GET_AUTO_GENERATED_KEYS)
1853
+ sequence_number = self._connection._get_new_sequence_number()
1854
+ self._out_message._send(sequence_number)
1855
+ self._in_message._read_message_sql(sequence_number)
1856
+ self._sqlcode = self._in_message.wire.header._get_function_code()
1857
+ if self._sqlcode != 100:
1858
+ raise DatabaseError(self._get_error_info(self._sqlcode))
1859
+ self._get_column_info(self._in_message.wire)
1860
+ self._lastrowid = self._in_message.wire._get()
1861
+ return self._lastrowid
1862
+
1863
+ def _cleanup(self):
1864
+ super()._cleanup()
1865
+ if self._rsrow != None:
1866
+ self._rsrow = None
1867
+ if self._params != None:
1868
+ self._params._clear()
1869
+ self._multiple_result_sets = False
1870
+ self._mrs_done = False
1871
+ self._fetch_done = False
1872
+ self._parameter_list_mismatch_exception = False
1873
+ self._parameter_sets = 0
1874
+ self._rowcount = -1
1875
+ self._exec_params = None
1876
+ self._statementType = StatementType.UPDATE
1877
+ self.statementFeatureOption = 0
1878
+ self.maxRowItemCount = 0
1879
+ self._is_batch_update = False
1880
+
1881
+ def close(self):
1882
+ if self._closed:
1883
+ return
1884
+ self._columns = None
1885
+ self._rowcount = -1
1886
+ self.arraysize = 0
1887
+
1888
+ self._connection = None
1889
+ self._in_message = None
1890
+ self._out_message = None
1891
+
1892
+ self._result_set = None
1893
+
1894
+ if self._rsrow != None:
1895
+ self._rsrow = None
1896
+ self._cursor_ptr = 0
1897
+ self._scroll_flag = False
1898
+ self._warehouse = []
1899
+ self._warehouse_dict = {}
1900
+ self._last_row_in_warehouse_dict = -1
1901
+ self._warehouse_dict_keys = []
1902
+
1903
+ self._params = None
1904
+ self._parsed_statement = None
1905
+ self._cursor_type = CursorType.DEFAULT
1906
+ self._statementType = StatementType.UPDATE # default
1907
+ self._paramInfo = None
1908
+ self.statement = None
1909
+ self.statementFeatureOption = Feature.optionNone
1910
+ self._statement_id = None
1911
+ self._sqlcode = None
1912
+ self._current_wire = None
1913
+ self._result_set = []
1914
+ self._rs_index = -1
1915
+
1916
+ self._parameter_sets = 0
1917
+ self._exec_params = None
1918
+ self._is_batch_update = False
1919
+ self._multiple_result_sets = False
1920
+ self._mrs_done = False
1921
+ self._fetch_done = False
1922
+ self._parameter_list_mismatch_exception = False
1923
+ if self._output_parameter_list != None:
1924
+ self._output_parameter_list._clear_list()
1925
+
1926
+ self._closed = True
1927
+
1928
+ def executemany(self, operation, seq_of_params):
1929
+ super().executemany(operation, seq_of_params)
1930
+ self._rowcount = 0
1931
+ for i in range(len(self.params)):
1932
+ self._rowcount += self._in_message.wire._get()
1933
+ return self._rowcount
1934
+
1935
+ def scroll(self, value, mode):
1936
+ if mode == None or mode == "":
1937
+ mode = "relative"
1938
+ mode = mode.lower()
1939
+ if mode != "absolute" and mode != "relative":
1940
+ raise ValueError("This mode is not supported - use 'relative' or 'absolute'.")
1941
+
1942
+ # Backward Scrolling
1943
+ if value < 0:
1944
+ if mode == "relative":
1945
+ self._rownumber = self._cursor_ptr + value - 1
1946
+ else:
1947
+ raise ValueError("Negative values with absolute scrolling are not allowed.")
1948
+ self._cursor_ptr = self._rownumber + 1
1949
+ if self._rs_index == 0:
1950
+ return self._warehouse[self._rownumber]
1951
+ else:
1952
+ if self._rownumber <= self._last_row_in_warehouse_dict:
1953
+ return self._retrieve_from_warehouse(self._rownumber)
1954
+ else:
1955
+ if self._current_wire == None:
1956
+ rows_available = self._last_row_in_warehouse_dict
1957
+ else:
1958
+ rows_available = self._last_row_in_warehouse_dict + len(self._warehouse)
1959
+ if self._rownumber <= rows_available:
1960
+ return self._warehouse[self._rownumber - self._last_row_in_warehouse_dict - 1]
1961
+ # Forward Scrolling
1962
+ else:
1963
+ if mode == "absolute":
1964
+ self._cursor_ptr = 0
1965
+ self._scroll_flag = True
1966
+ self._rownumber = self._cursor_ptr + value - 1
1967
+ if self._rs_index == 0:
1968
+ if self._rownumber >= len(self._warehouse):
1969
+ if mode == "absolute":
1970
+ self._cursor_ptr = len(self._warehouse)
1971
+ return self.fetchone()
1972
+ else:
1973
+ self._scroll_flag = False
1974
+ self._cursor_ptr = self._rownumber + 1
1975
+ return self._warehouse[self._rownumber]
1976
+ else:
1977
+ if self._rownumber <= self._last_row_in_warehouse_dict:
1978
+ self._scroll_flag = False
1979
+ self._cursor_ptr = self._rownumber + 1
1980
+ return self._retrieve_from_warehouse(self._rownumber)
1981
+ else:
1982
+ if self._current_wire == None:
1983
+ rows_available = self._last_row_in_warehouse_dict
1984
+ else:
1985
+ rows_available = self._last_row_in_warehouse_dict + len(self._warehouse)
1986
+ if self._rownumber <= rows_available:
1987
+ self._scroll_flag = False
1988
+ self._cursor_ptr = self._rownumber + 1
1989
+ return self._warehouse[self._rownumber - self._last_row_in_warehouse_dict - 1]
1990
+ else:
1991
+ if mode == "absolute":
1992
+ self._cursor_ptr = rows_available + 1
1993
+ return self.fetchone()
1994
+
1995
+ def _retrieve_from_warehouse(self, value):
1996
+ for idx, (key, val) in enumerate(self._warehouse_dict.items()):
1997
+ if value <= key:
1998
+ if idx != 0:
1999
+ prev_key = self._warehouse_dict_keys[idx - 1]
2000
+ return val[value - prev_key - 1]
2001
+ return val[value]
2002
+
2003
+ def _switch_buffer(self):
2004
+ if self._sqlcode == 0:
2005
+ with self._connection._lock:
2006
+ self._out_message.wire._write_header(_Message.FETCH_DATA)
2007
+ intersystems_iris._MessageHeader._MessageHeader._set_statement_id(
2008
+ self._out_message.wire.buffer, self._statement_id
2009
+ )
2010
+
2011
+ sequence_number = self._connection._get_new_sequence_number()
2012
+ self._out_message._send(sequence_number)
2013
+
2014
+ self._in_message._read_message_sql(sequence_number, self._statement_id, 0, [100])
2015
+ self._sqlcode = self._in_message.wire.header._get_function_code()
2016
+ self._process_sqlcode(self._sqlcode)
2017
+ self._result_set.append(self._in_message.wire)
2018
+
2019
+ if self._sqlcode == 404:
2020
+ self._query404()
2021
+ return
2022
+
2023
+ if self._rs_index + 1 == len(self._result_set):
2024
+ self._warehouse_dict[self._cursor_ptr] = self._warehouse
2025
+ self._warehouse_dict_keys = sorted(self._warehouse_dict.keys())
2026
+ self._last_row_in_warehouse_dict = self._warehouse_dict_keys[-1]
2027
+ self._current_wire = None
2028
+ else:
2029
+ self._warehouse_dict[self._cursor_ptr] = self._warehouse
2030
+ self._warehouse_dict_keys = sorted(self._warehouse_dict.keys())
2031
+ self._last_row_in_warehouse_dict = self._warehouse_dict_keys[-1]
2032
+ self._warehouse = []
2033
+ self._rsrow._new_buffer = True
2034
+ self._rs_index += 1
2035
+ self._current_wire = self._result_set[self._rs_index]
2036
+
2037
+ def fetchone_helper(self):
2038
+ row_indexing = True
2039
+ if self.statementFeatureOption & Feature.optionFastSelect == Feature.optionFastSelect:
2040
+ list_item = self._current_wire.list_item
2041
+ buffer = list_item.buffer
2042
+ length = list_item.list_buffer_end
2043
+
2044
+ if self._rsrow._new_buffer:
2045
+ prev_offset = list_item.next_offset
2046
+ self._rsrow._new_buffer = False
2047
+ else:
2048
+ prev_offset = self._rsrow._offsets._length
2049
+
2050
+ if prev_offset < length:
2051
+ if self._rsrow._fast_first_iter:
2052
+ self._rsrow._fast_first_iter = False
2053
+ else:
2054
+ if self._rsrow._new_buffer:
2055
+ list_item.buffer = buffer
2056
+ list_item.next_offset = prev_offset
2057
+ intersystems_iris._DBList._DBList._get_list_element(list_item)
2058
+ length = list_item.next_offset
2059
+ prev_offset = list_item.data_offset
2060
+ if list_item.data_length == 0: #
2061
+ for j in range(self._rsrow.colCount):
2062
+ rowItems[j] = -1
2063
+ self._rsrow._offsets = self._rsrow.update(rowItems) # ???
2064
+ return True
2065
+ else:
2066
+ if self._rsrow.rowItems != None:
2067
+ self._rsrow.rowItems[-1] = 0
2068
+ return False
2069
+
2070
+ self._rsrow._last_list_item = list_item
2071
+ self._rsrow._offsets = self._rsrow.DataRowFastSelect(self._rsrow, prev_offset, length, buffer)
2072
+ self._warehouse.append(self._rsrow._offsets)
2073
+
2074
+ if self._current_wire._is_end():
2075
+ self._switch_buffer()
2076
+
2077
+ else:
2078
+ row_indexing = self._rsrow.indexRow(self._current_wire.list_item)
2079
+ if row_indexing:
2080
+ self._warehouse.append(self._rsrow._offsets)
2081
+
2082
+ if self._rsrow.rowItems == None:
2083
+ return
2084
+
2085
+ if self._rsrow.rowItems[-1] >= self._current_wire.list_item.list_buffer_end:
2086
+ self._switch_buffer()
2087
+
2088
+ self._cursor_ptr += 1
2089
+ return row_indexing
2090
+
2091
+ def fetchone(self):
2092
+ if self._result_set == None:
2093
+ raise InterfaceError(
2094
+ "Either execute has not yet been called, or the previous call of execute did not return a result set"
2095
+ )
2096
+
2097
+ if self._current_wire == None and self._cursor_ptr > self._last_row_in_warehouse_dict:
2098
+ return None
2099
+
2100
+ retval = None
2101
+ if self._rs_index == 0:
2102
+ if self._cursor_ptr < len(self._warehouse):
2103
+ self._cursor_ptr += 1
2104
+ retval = self._warehouse[self._cursor_ptr - 1]
2105
+ else:
2106
+ rownumber = self._cursor_ptr
2107
+ if rownumber <= self._last_row_in_warehouse_dict:
2108
+ self._cursor_ptr += 1
2109
+ retval = self._retrieve_from_warehouse(rownumber)
2110
+ else:
2111
+ if self._current_wire == None:
2112
+ rows_available = self._last_row_in_warehouse_dict
2113
+ else:
2114
+ rows_available = self._last_row_in_warehouse_dict + len(self._warehouse)
2115
+ if rownumber <= rows_available:
2116
+ self._cursor_ptr += 1
2117
+ retval = self._warehouse[rownumber - self._last_row_in_warehouse_dict - 1]
2118
+
2119
+ if retval is None:
2120
+ if self._scroll_flag:
2121
+ while self._cursor_ptr <= self._rownumber:
2122
+ if self.fetchone_helper():
2123
+ retval = self._rsrow._offsets
2124
+ self._scroll_flag = False
2125
+ else:
2126
+ if self.fetchone_helper():
2127
+ retval = self._rsrow._offsets
2128
+
2129
+ if retval is None:
2130
+ return retval
2131
+ return retval.as_tuple()
2132
+ # return tuple(retval[:])
2133
+
2134
+ def fetchmany(self, size=None):
2135
+ if self._result_set == None:
2136
+ raise InterfaceError(
2137
+ "Either execute has not yet been called, or the previous call of execute did not return a result set"
2138
+ )
2139
+
2140
+ if self._current_wire == None:
2141
+ if self._cursor_ptr > self._last_row_in_warehouse_dict:
2142
+ return []
2143
+ if size is None:
2144
+ size = self.arraysize
2145
+ if self._rs_index == 0:
2146
+ if self._cursor_ptr < len(self._warehouse):
2147
+ rows = []
2148
+ for i in range(size):
2149
+ row = self._warehouse[self._cursor_ptr]
2150
+ rows.append(row[:])
2151
+ if self._cursor_ptr + 1 >= len(self._warehouse):
2152
+ self._cursor_ptr += 1
2153
+ break
2154
+ self._cursor_ptr += 1
2155
+ return rows
2156
+ else:
2157
+ rows = []
2158
+ for i in range(size):
2159
+ row = self._retrieve_from_warehouse(self._cursor_ptr)
2160
+ rows.append(row[:])
2161
+ if self._cursor_ptr + 1 > self._last_row_in_warehouse_dict:
2162
+ self._cursor_ptr += 1
2163
+ break
2164
+ self._cursor_ptr += 1
2165
+ return rows
2166
+
2167
+ if size is None:
2168
+ size = self.arraysize
2169
+
2170
+ rows = []
2171
+ for i in range(size):
2172
+ row = self.fetchone()
2173
+ if row is None:
2174
+ break
2175
+ rows.append(row)
2176
+ return rows
2177
+
2178
+ def fetchall(self):
2179
+ if self._result_set == None:
2180
+ raise InterfaceError(
2181
+ "Either execute has not yet been called, or the previous call of execute did not return a result set"
2182
+ )
2183
+
2184
+ if self._current_wire == None:
2185
+ if self._cursor_ptr > self._last_row_in_warehouse_dict:
2186
+ return []
2187
+ if self._rs_index == 0:
2188
+ if self._cursor_ptr < len(self._warehouse):
2189
+ rows = []
2190
+ while 1:
2191
+ row = self._warehouse[self._cursor_ptr]
2192
+ rows.append(row[:])
2193
+ if self._cursor_ptr + 1 >= len(self._warehouse):
2194
+ self._cursor_ptr += 1
2195
+ break
2196
+ self._cursor_ptr += 1
2197
+ return rows
2198
+ else:
2199
+ rows = []
2200
+ while 1:
2201
+ row = self._retrieve_from_warehouse(self._cursor_ptr)
2202
+ rows.append(row[:])
2203
+ if self._cursor_ptr + 1 > self._last_row_in_warehouse_dict:
2204
+ self._cursor_ptr += 1
2205
+ break
2206
+ self._cursor_ptr += 1
2207
+ return rows
2208
+
2209
+ rows = []
2210
+ while self._current_wire is not None:
2211
+ row = self.fetchone()
2212
+ if not row:
2213
+ break
2214
+ rows.append(row)
2215
+
2216
+ return rows
2217
+
2218
+
2219
+ class EmbdeddedCursor(_BaseCursor):
2220
+ embedded = True
2221
+ _result_set = None
2222
+
2223
+ def __init__(self, connection: _IRISEmbedded) -> None:
2224
+ super().__init__(connection)
2225
+ self._sql = connection.iris.sql
2226
+ self._iris = connection.iris
2227
+ self._closed = False
2228
+
2229
+ # $System.SQL.SetSelectMode(1 /* ODBC */)
2230
+ # $System.SQL.Util.SetOption("SelectMode", 1 /* ODBC */)
2231
+ connection.iris.system.SQL.SetSelectMode(1)
2232
+
2233
+ def _get_cached_info(self):
2234
+ return False
2235
+
2236
+ def _get_parameters(self, params_set=0):
2237
+ params = self._params.collect(params_set)
2238
+ # None = '', '' = b'\x00'
2239
+ _conv = {
2240
+ type(None): lambda v: "",
2241
+ str: lambda v: v or b"\x00",
2242
+ decimal.Decimal: lambda v: float(v),
2243
+ }
2244
+ params = [_conv[type(v)](v) if type(v) in _conv else v for v in params]
2245
+ return params
2246
+
2247
+ def _get_column_info(self):
2248
+ self._columns = []
2249
+ if self._result_set is None:
2250
+ return
2251
+
2252
+ metadata = self._result_set.ResultSet._GetMetadata()
2253
+ count = metadata.columnCount if metadata != "" and metadata is not None else 0
2254
+ for i in range(count):
2255
+ slotPosition = i + 1
2256
+ _column_info = metadata.columns.GetAt(slotPosition)
2257
+ name = _column_info.colName
2258
+ odbctype = _column_info.ODBCType
2259
+ if _column_info.scale in SQLType.__members__:
2260
+ # There is a bug on IRIS side, when it may return incorrectly when it passed that way NUMERIC(?, ?)
2261
+ precision = 15
2262
+ scale = 15
2263
+ else:
2264
+ precision = _column_info.precision or None
2265
+ scale = _column_info.scale or None
2266
+ nullable = _column_info.isNullable
2267
+ label = _column_info.label
2268
+ tableName = _column_info.tableName
2269
+ schema = _column_info.schemaName
2270
+ catalog = None
2271
+ additionalData = [
2272
+ _column_info.isAutoIncrement,
2273
+ _column_info.isCaseSensitive,
2274
+ _column_info.isCurrency,
2275
+ _column_info.isReadOnly,
2276
+ _column_info.isRowVersion,
2277
+ _column_info.isUnique,
2278
+ _column_info.isAliased,
2279
+ _column_info.isExpression,
2280
+ _column_info.isHidden,
2281
+ _column_info.isIdentity,
2282
+ _column_info.isKeyColumn,
2283
+ _column_info.isRowId,
2284
+ ]
2285
+ self._columns.append(
2286
+ intersystems_iris.dbapi._Column._Column(
2287
+ name,
2288
+ odbctype,
2289
+ precision,
2290
+ scale,
2291
+ nullable,
2292
+ label,
2293
+ tableName,
2294
+ schema,
2295
+ catalog,
2296
+ additionalData,
2297
+ slotPosition,
2298
+ )
2299
+ )
2300
+
2301
+ @property
2302
+ def lastrowid(self):
2303
+ return self._lastrowid
2304
+
2305
+ def _prepare_new(self):
2306
+ statement = self._parsed_statement
2307
+ sqlcode = 0
2308
+ message = None
2309
+ try:
2310
+ self._statement = self._sql.prepare(statement)
2311
+ except Exception as ex:
2312
+ sqlcode = ex.sqlcode
2313
+ message = ex.message
2314
+ self._process_sqlcode(sqlcode, message)
2315
+
2316
+ def _prepared_query_execute(self):
2317
+ self._rowcount = 0
2318
+ params = self._get_parameters()
2319
+ sqlcode = 0
2320
+ message = None
2321
+ try:
2322
+ self._result_set = self._statement.execute(*params)
2323
+ self._get_column_info()
2324
+ self._rowcount += self._result_set.ResultSet._ROWCOUNT
2325
+ except Exception as ex:
2326
+ sqlcode = ex.sqlcode
2327
+ message = ex.message
2328
+
2329
+ self._process_sqlcode(sqlcode, message)
2330
+
2331
+ def _send_direct_update_request(self):
2332
+ self._rowcount = 0
2333
+ self._lastrowid = None
2334
+ statement = self._parsed_statement
2335
+
2336
+ sets = self._parameter_sets or 1
2337
+ for i in range(sets):
2338
+ params = self._get_parameters(i)
2339
+
2340
+ sqlcode = 0
2341
+ message = None
2342
+ try:
2343
+ _result_set = self._sql.exec(statement, *params)
2344
+ self._rowcount += _result_set.ResultSet._ROWCOUNT
2345
+ self._lastrowid = _result_set.ResultSet._ROWID
2346
+ except Exception as ex:
2347
+ sqlcode = ex.sqlcode
2348
+ message = ex.message
2349
+
2350
+ self._process_sqlcode(sqlcode, message)
2351
+
2352
+ def _send_direct_query_request(self):
2353
+ self._rowcount = 0
2354
+ statement = self._parsed_statement
2355
+
2356
+ params = self._get_parameters()
2357
+ sqlcode = 0
2358
+ message = None
2359
+ try:
2360
+ self._result_set = self._sql.exec(statement, *params)
2361
+ self._rowcount = self._result_set.ResultSet._ROWCOUNT
2362
+ self._get_column_info()
2363
+ except Exception as ex:
2364
+ sqlcode = ex.sqlcode
2365
+ message = ex.message
2366
+ self._process_sqlcode(sqlcode, message)
2367
+
2368
+ def _prepared_update_execute(self):
2369
+ self._rowcount = 0
2370
+ self._lastrowid = None
2371
+ sets = self._parameter_sets or 1
2372
+ metadata = self._statement.Statement._Metadata.parameters
2373
+ param_types = [metadata.GetAt(i + 1).ODBCType for i in range(metadata.Size)]
2374
+
2375
+ stream_chunk_size = 32000
2376
+
2377
+ for i in range(sets):
2378
+ params = self._get_parameters(i)
2379
+ for ip, param in enumerate(params):
2380
+ if param_types[ip] in (SQLType.LONGVARBINARY, SQLType.LONGVARCHAR):
2381
+ stream_class = '%Stream.GlobalBinary' if param_types[ip] == SQLType.LONGVARBINARY else '%Stream.GlobalCharacter'
2382
+ stream = self._iris.cls(stream_class)._New()
2383
+ while param:
2384
+ stream.Write(param[:stream_chunk_size])
2385
+ param = param[stream_chunk_size:]
2386
+ params[ip] = stream
2387
+
2388
+ sqlcode = 0
2389
+ message = None
2390
+ try:
2391
+ _result_set = self._statement.execute(*params)
2392
+ self._rowcount += _result_set.ResultSet._ROWCOUNT
2393
+ self._lastrowid = _result_set.ResultSet._ROWID
2394
+ except Exception as ex:
2395
+ sqlcode = ex.sqlcode
2396
+ message = ex.message
2397
+ self._process_sqlcode(sqlcode, message)
2398
+
2399
+ def _send_direct_stored_procedure_request(self):
2400
+ sqlproc = self._parsed_statement
2401
+ self._rowcount = 0
2402
+ params = self._get_parameters()
2403
+ params_marks = ", ".join(["?"] * len(params))
2404
+ statement = f"CALL {sqlproc} ({params_marks})"
2405
+
2406
+ sqlcode = 0
2407
+ message = None
2408
+ try:
2409
+ self._result_set = self._sql.exec(statement, *params)
2410
+ self._rowcount = self._result_set.ResultSet._ROWCOUNT
2411
+ self._get_column_info()
2412
+ except Exception as ex:
2413
+ sqlcode = ex.sqlcode
2414
+ message = ex.message
2415
+ self._process_sqlcode(sqlcode, message)
2416
+
2417
+ @property
2418
+ def rowcount(self):
2419
+ return self._rowcount
2420
+
2421
+ def fetchone(self):
2422
+ if self._result_set == None:
2423
+ raise InterfaceError(
2424
+ "Either execute has not yet been called, or the previous call of execute did not return a result set"
2425
+ )
2426
+
2427
+ try:
2428
+ values = self._result_set.__next__()
2429
+ except:
2430
+ return None
2431
+
2432
+ values = [None if v == "" else "" if v == "\x00" else v for v in values]
2433
+ row = namedtuple("Row", [col.name for col in self._columns], rename=True)
2434
+
2435
+ _types = {
2436
+ SQLType.BIGINT: int,
2437
+ SQLType.BINARY: bytes,
2438
+ SQLType.VARBINARY: bytes,
2439
+ SQLType.BIT: bool,
2440
+ SQLType.FLOAT: float,
2441
+ SQLType.NUMERIC: decimal.Decimal,
2442
+ SQLType.INTEGER: int,
2443
+ SQLType.VARCHAR: str,
2444
+ SQLType.LONGVARBINARY: IRISBinaryStream,
2445
+ SQLType.LONGVARCHAR: IRISStream,
2446
+ }
2447
+
2448
+ if self._columns:
2449
+ for _column in self._columns:
2450
+ value = values[_column.slotPosition - 1]
2451
+
2452
+ ctype = _column.type
2453
+ value_type = _types[ctype] if ctype in _types else None
2454
+ try:
2455
+ if not _column.tableName and not _column.schema:
2456
+ if type(value) == float:
2457
+ value = decimal.Decimal(str(value))
2458
+ elif value is None or value_type is None:
2459
+ pass
2460
+ elif value_type is bytes:
2461
+ value = bytes(map(ord, value))
2462
+ elif value_type is decimal.Decimal:
2463
+ value = decimal.Decimal(str(value))
2464
+ elif issubclass(value_type, IRISStream):
2465
+ stream = value_type(self._connection, value, embedded=True)
2466
+ value = stream.fetch()
2467
+ elif not isinstance(value, value_type):
2468
+ value = value_type(value)
2469
+ except Exception as ex:
2470
+ raise ex
2471
+ pass
2472
+ values[_column.slotPosition - 1] = value
2473
+ return row(*values)
2474
+
2475
+ def fetchall(self):
2476
+ if self._result_set == None:
2477
+ raise InterfaceError(
2478
+ "Either execute has not yet been called, or the previous call of execute did not return a result set"
2479
+ )
2480
+
2481
+ rows = []
2482
+ while True:
2483
+ row = self.fetchone()
2484
+ if not row:
2485
+ break
2486
+ rows.append(row)
2487
+ return rows
2488
+
2489
+ def fetchmany(self, size=None):
2490
+ if self._result_set == None:
2491
+ raise InterfaceError(
2492
+ "Either execute has not yet been called, or the previous call of execute did not return a result set"
2493
+ )
2494
+
2495
+ if size is None:
2496
+ size = self.arraysize
2497
+
2498
+ rows = []
2499
+ for i in range(size):
2500
+ row = self.fetchone()
2501
+ if row is None:
2502
+ break
2503
+ rows.append(row)
2504
+ return rows
2505
+
2506
+ def nextset(self):
2507
+ raise NotImplementedErrorDBAPI()
2508
+
2509
+
2510
+ # Type Objects
2511
+ def Date(year, month, day):
2512
+ raise NotImplementedErrorDBAPI()
2513
+
2514
+
2515
+ def Time(hour, minutes, second):
2516
+ raise NotImplementedErrorDBAPI()
2517
+
2518
+
2519
+ def Timestamp(year, month, day, hour, minute, second):
2520
+ raise NotImplementedErrorDBAPI()
2521
+
2522
+
2523
+ def DateFromTicks(ticks):
2524
+ raise NotImplementedErrorDBAPI()
2525
+
2526
+
2527
+ def TimeFromTicks(ticks):
2528
+ raise NotImplementedErrorDBAPI()
2529
+
2530
+
2531
+ def TimestampFromTicks(ticks):
2532
+ raise NotImplementedErrorDBAPI()
2533
+
2534
+
2535
+ # def Binary(string):
2536
+ # return string
2537
+
2538
+ # Type definitions.
2539
+ Binary = bytes
2540
+
2541
+ STRING = str
2542
+ BINARY = bytes
2543
+ NUMBER = float
2544
+ ROWID = str
2545
+
2546
+ # still needs type singletons (?)
2547
+
2548
+
2549
+ # Exception architecture
2550
+ class Error(Exception):
2551
+ pass
2552
+
2553
+
2554
+ class Warning(Exception):
2555
+ pass
2556
+
2557
+
2558
+ class InterfaceError(Error):
2559
+ pass
2560
+
2561
+
2562
+ class DatabaseError(Error):
2563
+ pass
2564
+
2565
+
2566
+ class InternalError(DatabaseError):
2567
+ pass
2568
+
2569
+
2570
+ class OperationalError(DatabaseError):
2571
+ pass
2572
+
2573
+
2574
+ class ProgrammingError(DatabaseError):
2575
+ pass
2576
+
2577
+
2578
+ class IntegrityError(DatabaseError):
2579
+ pass
2580
+
2581
+
2582
+ class DataError(DatabaseError):
2583
+ pass
2584
+
2585
+
2586
+ class NotSupportedError(DatabaseError):
2587
+ pass