sqlalchemy-iris 0.15.5b1__tar.gz → 0.16.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. {sqlalchemy_iris-0.15.5b1/sqlalchemy_iris.egg-info → sqlalchemy_iris-0.16.0}/PKG-INFO +3 -2
  2. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/_Column.py +3 -0
  3. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/_DBAPI.py +38 -4
  4. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/_IRISStream.py +2 -0
  5. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/_ResultSetRow.py +1 -1
  6. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/preparser/_PreParser.py +52 -3
  7. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/preparser/_Token.py +3 -0
  8. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/preparser/_TokenList.py +3 -0
  9. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/setup.cfg +1 -1
  10. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris/base.py +62 -3
  11. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris/information_schema.py +2 -1
  12. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris/requirements.py +16 -0
  13. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0/sqlalchemy_iris.egg-info}/PKG-INFO +3 -2
  14. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris.egg-info/SOURCES.txt +0 -6
  15. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris.egg-info/top_level.txt +0 -1
  16. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/tests/test_alembic.py +6 -4
  17. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/tests/test_suite.py +32 -2
  18. sqlalchemy_iris-0.15.5b1/iris/__init__.py +0 -66
  19. sqlalchemy_iris-0.15.5b1/iris/__init__.pyi +0 -236
  20. sqlalchemy_iris-0.15.5b1/iris/_cli.py +0 -75
  21. sqlalchemy_iris-0.15.5b1/iris/iris_ipm.py +0 -40
  22. sqlalchemy_iris-0.15.5b1/iris/iris_ipm.pyi +0 -17
  23. sqlalchemy_iris-0.15.5b1/iris/iris_utils.py +0 -336
  24. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/LICENSE +0 -0
  25. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/README.md +0 -0
  26. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_BufferReader.py +0 -0
  27. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_BufferWriter.py +0 -0
  28. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_ConnectionInformation.py +0 -0
  29. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_ConnectionParameters.py +0 -0
  30. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_Constant.py +0 -0
  31. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_DBList.py +0 -0
  32. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_Device.py +0 -0
  33. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_GatewayContext.py +0 -0
  34. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_GatewayException.py +0 -0
  35. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_GatewayUtility.py +0 -0
  36. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_IRIS.py +0 -0
  37. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_IRISConnection.py +0 -0
  38. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_IRISEmbedded.py +0 -0
  39. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_IRISGlobalNode.py +0 -0
  40. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_IRISGlobalNodeView.py +0 -0
  41. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_IRISIterator.py +0 -0
  42. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_IRISList.py +0 -0
  43. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_IRISNative.py +0 -0
  44. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_IRISOREF.py +0 -0
  45. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_IRISObject.py +0 -0
  46. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_IRISReference.py +0 -0
  47. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_InStream.py +0 -0
  48. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_LegacyIterator.py +0 -0
  49. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_ListItem.py +0 -0
  50. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_ListReader.py +0 -0
  51. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_ListWriter.py +0 -0
  52. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_LogFileStream.py +0 -0
  53. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_MessageHeader.py +0 -0
  54. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_OutStream.py +0 -0
  55. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_PrintStream.py +0 -0
  56. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_PythonGateway.py +0 -0
  57. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/_SharedMemorySocket.py +0 -0
  58. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/__init__.py +0 -0
  59. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/__main__.py +0 -0
  60. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/_Descriptor.py +0 -0
  61. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/_Message.py +0 -0
  62. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/_Parameter.py +0 -0
  63. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/_ParameterCollection.py +0 -0
  64. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/_SQLType.py +0 -0
  65. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/__init__.py +0 -0
  66. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/preparser/_Scanner.py +0 -0
  67. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/dbapi/preparser/__init__.py +0 -0
  68. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/_BusinessHost.py +0 -0
  69. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/_BusinessOperation.py +0 -0
  70. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/_BusinessProcess.py +0 -0
  71. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/_BusinessService.py +0 -0
  72. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/_Common.py +0 -0
  73. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/_Director.py +0 -0
  74. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/_IRISBusinessOperation.py +0 -0
  75. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/_IRISBusinessService.py +0 -0
  76. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/_IRISInboundAdapter.py +0 -0
  77. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/_IRISOutboundAdapter.py +0 -0
  78. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/_InboundAdapter.py +0 -0
  79. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/_Message.py +0 -0
  80. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/_OutboundAdapter.py +0 -0
  81. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/intersystems_iris/pex/__init__.py +0 -0
  82. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/irisnative/_IRISNative.py +0 -0
  83. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/irisnative/__init__.py +0 -0
  84. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/setup.py +0 -0
  85. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris/__init__.py +0 -0
  86. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris/alembic.py +0 -0
  87. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris/embedded.py +0 -0
  88. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris/iris.py +0 -0
  89. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris/irisasync.py +0 -0
  90. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris/provision.py +0 -0
  91. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris/types.py +0 -0
  92. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris.egg-info/dependency_links.txt +0 -0
  93. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris.egg-info/entry_points.txt +0 -0
  94. {sqlalchemy_iris-0.15.5b1 → sqlalchemy_iris-0.16.0}/sqlalchemy_iris.egg-info/requires.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: sqlalchemy-iris
3
- Version: 0.15.5b1
3
+ Version: 0.16.0
4
4
  Summary: InterSystems IRIS for SQLAlchemy
5
5
  Home-page: https://github.com/caretdev/sqlalchemy-iris
6
6
  Maintainer: CaretDev
@@ -23,6 +23,7 @@ Requires-Python: >=3.8
23
23
  Description-Content-Type: text/markdown
24
24
  License-File: LICENSE
25
25
  Requires-Dist: SQLAlchemy>=1.3
26
+ Dynamic: requires-dist
26
27
 
27
28
  sqlalchemy-iris
28
29
  ===
@@ -54,3 +54,6 @@ class _Column(intersystems_iris.dbapi._Descriptor._Descriptor):
54
54
  self.isKeyColumn,
55
55
  self.isRowId]
56
56
  return _Column(self.name, self.type, self.precision, self.scale, self.nullable, self.label, self.tableName, self.schema, self.catalog, additionalData, self.slotPosition)
57
+
58
+ def __repr__(self) -> str:
59
+ return f"<Column name='{self.name}' type='{self.type}'>"
@@ -14,7 +14,7 @@ from ._ResultSetRow import _ResultSetRow
14
14
  import intersystems_iris.dbapi._ParameterCollection
15
15
  import intersystems_iris.dbapi.preparser._PreParser
16
16
  from intersystems_iris.dbapi._Parameter import ParameterMode
17
- from intersystems_iris.dbapi.preparser._PreParser import StatementType, MultiValuesInsert
17
+ from intersystems_iris.dbapi.preparser._PreParser import StatementType, MultiValuesInsert, InsertWithReturning
18
18
  from intersystems_iris._IRISConnection import Feature
19
19
  from intersystems_iris._InStream import _InStream
20
20
  from intersystems_iris.dbapi._IRISStream import (
@@ -252,6 +252,11 @@ class _BaseCursor:
252
252
  self._cleanup()
253
253
  try:
254
254
  self._preparse()
255
+ except InsertWithReturning as ex:
256
+ ids = intersystems_iris._ListWriter._ListWriter()
257
+ self.execute(ex.insert_query, params)
258
+ ids._set(self.lastrowid)
259
+ return self.execute(ex.select_query, bytes(ids._get_buffer()))
255
260
  except MultiValuesInsert as ex:
256
261
  # convert to executemany
257
262
  params = params or ex.params
@@ -269,6 +274,7 @@ class _BaseCursor:
269
274
 
270
275
  self._execute()
271
276
  return self._rowcount
277
+ # return self.execute(returning_query)
272
278
 
273
279
  def add_batch(self):
274
280
  self._is_alive()
@@ -321,7 +327,17 @@ class _BaseCursor:
321
327
  self._cleanup()
322
328
  self._is_batch_update = True
323
329
 
324
- self._preparse()
330
+ try:
331
+ self._preparse()
332
+ except InsertWithReturning as ex:
333
+ ids = intersystems_iris._ListWriter._ListWriter()
334
+ for params in seq_of_params:
335
+ self.execute(ex.insert_query, params)
336
+ ids._set(self.lastrowid)
337
+ self.execute(ex.select_query, bytes(ids._get_buffer()))
338
+ raise
339
+ except Exception:
340
+ raise
325
341
  self._prepare()
326
342
 
327
343
  for row_num, param_row in enumerate(seq_of_params):
@@ -389,6 +405,8 @@ class _BaseCursor:
389
405
  )
390
406
  try:
391
407
  pOut = parser.PreParse(self.statement, self._params)
408
+ except InsertWithReturning:
409
+ raise
392
410
  except MultiValuesInsert:
393
411
  raise
394
412
  except Exception as e:
@@ -1182,6 +1200,8 @@ class Cursor(_BaseCursor):
1182
1200
  sets = self._parameter_sets or 1
1183
1201
  self.params = list(self.params).copy()
1184
1202
  param_types = [param.type for param in self._params._params_list]
1203
+ if not self.params:
1204
+ return
1185
1205
 
1186
1206
  for i in range(sets):
1187
1207
  params = self._params.collect(i)
@@ -1949,7 +1969,12 @@ class Cursor(_BaseCursor):
1949
1969
  self._closed = True
1950
1970
 
1951
1971
  def executemany(self, operation, seq_of_params):
1952
- super().executemany(operation, seq_of_params)
1972
+ try:
1973
+ super().executemany(operation, seq_of_params)
1974
+ except InsertWithReturning:
1975
+ return
1976
+ except Exception:
1977
+ raise
1953
1978
  self._rowcount = 0
1954
1979
  for i in range(len(self.params)):
1955
1980
  self._rowcount += self._in_message.wire._get()
@@ -2263,13 +2288,21 @@ class EmbdeddedCursor(_BaseCursor):
2263
2288
  def __del__(self):
2264
2289
  try:
2265
2290
  self.close()
2266
- except:
2291
+ except Exception:
2267
2292
  pass
2268
2293
  return
2269
2294
 
2270
2295
  def _get_cached_info(self):
2271
2296
  return False
2272
2297
 
2298
+ def executemany(self, operation, seq_of_params):
2299
+ try:
2300
+ super().executemany(operation, seq_of_params)
2301
+ except InsertWithReturning:
2302
+ return
2303
+ except Exception:
2304
+ raise
2305
+
2273
2306
  def _get_parameters(self, params_set=0):
2274
2307
  params = self._params.collect(params_set)
2275
2308
  # None = '', '' = b'\x00'
@@ -2394,6 +2427,7 @@ class EmbdeddedCursor(_BaseCursor):
2394
2427
  sqlcode = 0
2395
2428
  message = None
2396
2429
  try:
2430
+ print("!!!! _send_direct_query_request:params", [statement, params])
2397
2431
  self._result_set = self._sql.exec(statement, *params)
2398
2432
  self._rowcount = self._result_set.ResultSet._ROWCOUNT
2399
2433
  self._get_column_info()
@@ -58,6 +58,8 @@ class IRISStream:
58
58
  if not self._binary:
59
59
  result = str(result, self._locale)
60
60
 
61
+ if self._binary:
62
+ return bytes(result)
61
63
  return result
62
64
 
63
65
 
@@ -162,7 +162,7 @@ class _ResultSetRow:
162
162
  self._list_item.next_offset = self._offsets[key]
163
163
  _DBList._get_list_element(self._list_item)
164
164
  item = _DBList._get(self._list_item, self._locale)
165
- _column: _Column = self._columns[idx]
165
+ _column: _Column = self._columns[key]
166
166
  ctype = _column.type
167
167
  value_type = self._types[ctype] if ctype in self._types else None
168
168
  try:
@@ -11,6 +11,7 @@ from intersystems_iris.dbapi._Parameter import ParameterMode
11
11
  from intersystems_iris.dbapi.preparser._Token import TOKEN
12
12
  from intersystems_iris.dbapi.preparser._Scanner import ParseToken
13
13
 
14
+
14
15
  class MultiValuesInsert(Exception):
15
16
 
16
17
  def __init__(self, *args: object, query: str, rows: int, params=None) -> None:
@@ -20,6 +21,15 @@ class MultiValuesInsert(Exception):
20
21
  self.params = params
21
22
 
22
23
 
24
+ class InsertWithReturning(Exception):
25
+
26
+ def __init__(self, *args: object, insert_query: str, insert_params, select_query: str) -> None:
27
+ super().__init__(*args)
28
+ self.insert_query = insert_query
29
+ self.insert_params = insert_params
30
+ self.select_query = select_query
31
+
32
+
23
33
  # May want to move to its own file eventually
24
34
  # SQL Statement Types
25
35
  class StatementType(enum.IntEnum):
@@ -480,6 +490,11 @@ class _PreParser(object):
480
490
  if found_insert:
481
491
  self.Tokenize(t_query)
482
492
 
493
+ _, t_query, t_select_query = self.InsertReturning(t_query)
494
+ if t_select_query:
495
+ raise InsertWithReturning(insert_query=t_query, insert_params=p_Parameters, select_query=t_select_query)
496
+ self.Tokenize(t_query)
497
+
483
498
  # Resolve the tokens and determine output
484
499
  return self.Resolve(t_query, p_Parameters)
485
500
 
@@ -549,9 +564,39 @@ class _PreParser(object):
549
564
  new_query += _query(False)
550
565
 
551
566
  return found, new_query
552
- except:
567
+ except Exception:
553
568
  return False, query
554
-
569
+
570
+ def InsertReturning(self, query):
571
+ plain_query = ''
572
+
573
+ tokens = self.m_Tokens.GetEnumerator()
574
+ keywords = list(self.s_StatementTable.keys()) + list(self.s_ParsedStatements.keys()) + list(self.s_TransactionStatements.keys())
575
+ while tokens.MoveNext() and not tokens.Current().UpperOnOf(keywords):
576
+ plain_query += tokens.Current().Lexeme + ' '
577
+ if tokens.AtEnd() or not tokens.Current().UpperEquals("INSERT"):
578
+ return False, query, None
579
+
580
+ if not tokens.MoveNext() or not tokens.Current().UpperEquals("INTO"):
581
+ return False, query, None
582
+ plain_query += 'INSERT INTO '
583
+ table_name = ""
584
+ while tokens.MoveNext() and not tokens.Current().UpperOnOf(["(", "SET", "VALUES", "DEFAULT"]):
585
+ table_name += tokens.Current().Lexeme + ' '
586
+ plain_query += table_name + ' ' + tokens.Current().Lexeme + ' '
587
+ while tokens.MoveNext() and not tokens.Current().UpperEquals("RETURNING"):
588
+ plain_query += tokens.Current().Lexeme + ' '
589
+
590
+ select_query = None
591
+ if not tokens.AtEnd():
592
+ select_query = 'SELECT '
593
+ while tokens.MoveNext():
594
+ select_query += tokens.Current().Lexeme + ' '
595
+ select_query += "FROM " + table_name
596
+ select_query += " WHERE %ID %INLIST ?"
597
+
598
+ return False, plain_query, select_query
599
+
555
600
  def InsertMultiValues(self, query):
556
601
  new_query = ''
557
602
  values_list = []
@@ -580,7 +625,10 @@ class _PreParser(object):
580
625
  break
581
626
  if token.TokenType is TOKEN.CONSTANT:
582
627
  values += '?'
583
- params += [token.Lexeme]
628
+ param = token.Lexeme
629
+ if param.__len__ and param[0] == "'" and param[0] == param[-1]:
630
+ param = param[1: -1]
631
+ params += [param]
584
632
  else:
585
633
  values += token.Lexeme
586
634
  values += ' '
@@ -1047,6 +1095,7 @@ class _PreParser(object):
1047
1095
  nParamIndex += 1
1048
1096
  if nParamIndex == length + 1:
1049
1097
  break
1098
+
1050
1099
  return pOut
1051
1100
 
1052
1101
  # '?' represents a parameter; adds a parameter to p_Parameters if none were provided
@@ -76,6 +76,9 @@ It records the classification of the token as well as retaining the original str
76
76
  def UpperEquals(self, p_str):
77
77
  return self.UpperLexeme == p_str
78
78
 
79
+ def UpperOnOf(self, p_str_list):
80
+ return self.UpperLexeme in p_str_list
81
+
79
82
  def UpperContains(self, p_str):
80
83
  return p_str in self.UpperLexeme
81
84
 
@@ -216,6 +216,9 @@ class LinkedListEnumerator(ITokenEnumerator):
216
216
  def Current(self):
217
217
  return self.m_Current.GetValue()
218
218
 
219
+ def AtEnd(self):
220
+ return self.m_bEOF
221
+
219
222
  def MoveNext(self):
220
223
  if self.m_bEOF:
221
224
  return False
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = sqlalchemy-iris
3
- version = 0.15.5b1
3
+ version = 0.16.0
4
4
  description = InterSystems IRIS for SQLAlchemy
5
5
  long_description = file: README.md
6
6
  url = https://github.com/caretdev/sqlalchemy-iris
@@ -653,6 +653,20 @@ class IRISDDLCompiler(sql.compiler.DDLCompiler):
653
653
  def visit_check_constraint(self, constraint, **kw):
654
654
  pass
655
655
 
656
+ def create_table_constraints(self, table, **kw):
657
+ description = ""
658
+ comment = table.comment
659
+ if comment:
660
+ # hack to keep \r, kind of
661
+ comment = comment.replace('\r', '\n\t')
662
+ literal = self.sql_compiler.render_literal_value(comment, sqltypes.String())
663
+ description = "%DESCRIPTION " + literal
664
+
665
+ constraints = super().create_table_constraints(table, **kw)
666
+ if constraints and description:
667
+ description = ", \n\t" + description
668
+ return constraints + description
669
+
656
670
  def visit_add_constraint(self, create, **kw):
657
671
  if isinstance(create.element, schema.CheckConstraint):
658
672
  raise exc.CompileError("Can't add CHECK constraint")
@@ -701,6 +715,7 @@ class IRISDDLCompiler(sql.compiler.DDLCompiler):
701
715
 
702
716
  comment = column.comment
703
717
  if comment is not None:
718
+ comment = comment.replace('\r', '\n\t')
704
719
  literal = self.sql_compiler.render_literal_value(comment, sqltypes.String())
705
720
  colspec.append("%DESCRIPTION " + literal)
706
721
 
@@ -883,6 +898,12 @@ class IRISDialect(default.DefaultDialect):
883
898
  type_compiler = IRISTypeCompiler
884
899
  execution_ctx_cls = IRISExecutionContext
885
900
 
901
+ insert_returning = True
902
+ insert_executemany_returning = True
903
+ insert_executemany_returning_sort_by_parameter_order = True
904
+ update_executemany_returning = False
905
+ delete_executemany_returning = False
906
+
886
907
  construct_arguments = [
887
908
  (schema.Index, {"include": None}),
888
909
  ]
@@ -1105,6 +1126,38 @@ There are no access to %Dictionary, may be required for some advanced features,
1105
1126
  return "SQLUser"
1106
1127
  return schema
1107
1128
 
1129
+ @reflection.cache
1130
+ def get_table_options(self, connection, table_name, schema=None, **kw):
1131
+ if not self.has_table(connection=connection, table_name=table_name, schema=schema):
1132
+ raise exc.NoSuchTableError(
1133
+ f"{schema}.{table_name}" if schema else table_name
1134
+ ) from None
1135
+ return {}
1136
+
1137
+ @reflection.cache
1138
+ def get_table_comment(self, connection, table_name, schema=None, **kw):
1139
+ if not self.has_table(connection=connection, table_name=table_name, schema=schema):
1140
+ raise exc.NoSuchTableError(
1141
+ f"{schema}.{table_name}" if schema else table_name
1142
+ ) from None
1143
+
1144
+ tables = ischema.tables
1145
+ schema_name = self.get_schema(schema)
1146
+
1147
+ s = sql.select(tables.c.description).where(
1148
+ sql.and_(
1149
+ tables.c.table_schema == str(schema_name),
1150
+ tables.c.table_name == str(table_name),
1151
+ )
1152
+ )
1153
+ comment = connection.execute(s).scalar()
1154
+ if comment:
1155
+ # make it as \r
1156
+ comment = comment.replace(' \t\t\t\t', '\r')
1157
+ # restore \n
1158
+ comment = comment.replace(' \t\t\t', '\n')
1159
+ return {"text": comment}
1160
+
1108
1161
  @reflection.cache
1109
1162
  def get_schema_names(self, connection, **kw):
1110
1163
  s = sql.select(ischema.schemata.c.schema_name).order_by(
@@ -1131,7 +1184,7 @@ There are no access to %Dictionary, may be required for some advanced features,
1131
1184
  return table_names
1132
1185
 
1133
1186
  @reflection.cache
1134
- def get_temp_table_names(self, connection, dblink=None, **kw):
1187
+ def get_temp_table_names(self, connection, **kw):
1135
1188
  tables = ischema.tables
1136
1189
  s = (
1137
1190
  sql.select(tables.c.table_name)
@@ -1598,6 +1651,7 @@ There are no access to %Dictionary, may be required for some advanced features,
1598
1651
  columns.c.column_default,
1599
1652
  columns.c.collation_name,
1600
1653
  columns.c.auto_increment,
1654
+ columns.c.description,
1601
1655
  )
1602
1656
  .select_from(columns)
1603
1657
  .where(
@@ -1650,7 +1704,12 @@ There are no access to %Dictionary, may be required for some advanced features,
1650
1704
  sqlComputeCode = row[property.c.SqlComputeCode]
1651
1705
  calculated = row[property.c.Calculated]
1652
1706
  transient = row[property.c.Transient]
1653
- # description = row[columns.c.description]
1707
+ comment = row[columns.c.description]
1708
+ if comment:
1709
+ # make it as \r
1710
+ comment = comment.replace(' \t\t\t\t', '\r')
1711
+ # restore \n
1712
+ comment = comment.replace(' \t\t\t', '\n')
1654
1713
 
1655
1714
  coltype = self.ischema_names.get(type_, None)
1656
1715
 
@@ -1696,7 +1755,7 @@ There are no access to %Dictionary, may be required for some advanced features,
1696
1755
  "nullable": nullable,
1697
1756
  "default": default,
1698
1757
  "autoincrement": autoincrement,
1699
- # "comment": description,
1758
+ "comment": comment,
1700
1759
  }
1701
1760
  if sqlComputeCode and "set {*} = " in sqlComputeCode.lower():
1702
1761
  sqltext = sqlComputeCode
@@ -44,6 +44,7 @@ tables = Table(
44
44
  Column("TABLE_NAME", String, key="table_name"),
45
45
  Column("TABLE_TYPE", String, key="table_type"),
46
46
  Column("CLASSNAME", String, key="classname"),
47
+ Column("DESCRIPTION", String, key="description"),
47
48
  schema="INFORMATION_SCHEMA",
48
49
  )
49
50
 
@@ -69,7 +70,7 @@ columns = Table(
69
70
  Column("AUTO_INCREMENT", YESNO, key="auto_increment"),
70
71
  Column("UNIQUE_COLUMN", YESNO, key="unique_column"),
71
72
  Column("PRIMARY_KEY", YESNO, key="primary_key"),
72
- Column("DESCIPTION", String, key="desciption"),
73
+ Column("DESCRIPTION", String, key="description"),
73
74
  schema="INFORMATION_SCHEMA",
74
75
  )
75
76
  property_definition = Table(
@@ -58,6 +58,22 @@ class Requirements(SuiteRequirements, AlembicRequirements):
58
58
 
59
59
  return exclusions.open()
60
60
 
61
+ @property
62
+ def reflect_table_options(self):
63
+ return exclusions.open()
64
+
65
+ @property
66
+ def comment_reflection(self):
67
+ return exclusions.open()
68
+
69
+ @property
70
+ def insert_returning(self):
71
+ return exclusions.open()
72
+
73
+ @property
74
+ def unusual_column_name_characters(self):
75
+ return exclusions.open()
76
+
61
77
  @property
62
78
  def computed_columns(self):
63
79
  "Supports computed columns"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: sqlalchemy-iris
3
- Version: 0.15.5b1
3
+ Version: 0.16.0
4
4
  Summary: InterSystems IRIS for SQLAlchemy
5
5
  Home-page: https://github.com/caretdev/sqlalchemy-iris
6
6
  Maintainer: CaretDev
@@ -23,6 +23,7 @@ Requires-Python: >=3.8
23
23
  Description-Content-Type: text/markdown
24
24
  License-File: LICENSE
25
25
  Requires-Dist: SQLAlchemy>=1.3
26
+ Dynamic: requires-dist
26
27
 
27
28
  sqlalchemy-iris
28
29
  ===
@@ -65,12 +65,6 @@ intersystems_iris/pex/_InboundAdapter.py
65
65
  intersystems_iris/pex/_Message.py
66
66
  intersystems_iris/pex/_OutboundAdapter.py
67
67
  intersystems_iris/pex/__init__.py
68
- iris/__init__.py
69
- iris/__init__.pyi
70
- iris/_cli.py
71
- iris/iris_ipm.py
72
- iris/iris_ipm.pyi
73
- iris/iris_utils.py
74
68
  irisnative/_IRISNative.py
75
69
  irisnative/__init__.py
76
70
  sqlalchemy_iris/__init__.py
@@ -1,4 +1,3 @@
1
1
  intersystems_iris
2
- iris
3
2
  irisnative
4
3
  sqlalchemy_iris
@@ -70,7 +70,9 @@ else:
70
70
  self.meta.drop_all(connection)
71
71
 
72
72
  def test_drop_col_with_fk(self, ops_context, connection, tables):
73
- ops_context.drop_column("round_trip_table", "oid_fk", schema=self.meta.schema)
73
+ ops_context.drop_column(
74
+ "round_trip_table", "oid_fk", schema=self.meta.schema
75
+ )
74
76
  insp = inspect(connection)
75
77
  eq_(insp.get_foreign_keys("round_trip_table", schema=self.meta.schema), [])
76
78
 
@@ -112,9 +114,9 @@ else:
112
114
 
113
115
  result = connection.execute(text("select col from tab")).all()
114
116
  assert result == [
115
- (bytearray(b"'some data 1'"),),
116
- (bytearray(b"'some data 2'"),),
117
- (bytearray(b"'some data 3'"),),
117
+ (b"some data 1",),
118
+ (b"some data 2",),
119
+ (b"some data 3",),
118
120
  ]
119
121
 
120
122
  insp = inspect(connection)
@@ -2,6 +2,8 @@ from enum import Enum
2
2
 
3
3
  from sqlalchemy.testing.suite import FetchLimitOffsetTest as _FetchLimitOffsetTest
4
4
  from sqlalchemy.testing.suite import CompoundSelectTest as _CompoundSelectTest
5
+ from sqlalchemy.testing.suite import TableDDLTest as _TableDDLTest
6
+ from sqlalchemy.testing.suite import FutureTableDDLTest as _FutureTableDDLTest
5
7
  from sqlalchemy.testing.suite import CTETest as _CTETest
6
8
  from sqlalchemy.testing.suite import DifficultParametersTest as _DifficultParametersTest
7
9
  from sqlalchemy.testing import fixtures
@@ -63,9 +65,19 @@ class CTETest(_CTETest):
63
65
  pass
64
66
 
65
67
 
66
- @pytest.mark.skip()
67
68
  class DifficultParametersTest(_DifficultParametersTest):
68
- pass
69
+
70
+ tough_parameters = _DifficultParametersTest.tough_parameters
71
+
72
+ @tough_parameters
73
+ def test_round_trip_same_named_column(
74
+ self, paramname, connection, metadata
75
+ ):
76
+ if paramname == 'dot.s':
77
+ # not supported
78
+ pytest.skip()
79
+ return
80
+ super().test_round_trip_same_named_column(paramname, connection, metadata)
69
81
 
70
82
 
71
83
  class FetchLimitOffsetTest(_FetchLimitOffsetTest):
@@ -486,3 +498,21 @@ class ConcatTest(fixtures.TablesTest):
486
498
  ("sometestdata",),
487
499
  ],
488
500
  )
501
+
502
+
503
+ class TableDDLTest(_TableDDLTest):
504
+ # IRIS does not want to update comments on the fly
505
+ def test_add_table_comment(self, connection):
506
+ pass
507
+
508
+ def test_drop_table_comment(self, connection):
509
+ pass
510
+
511
+
512
+ class FutureTableDDLTest(_FutureTableDDLTest):
513
+ # IRIS does not want to update comments on the fly
514
+ def test_add_table_comment(self, connection):
515
+ pass
516
+
517
+ def test_drop_table_comment(self, connection):
518
+ pass
@@ -1,66 +0,0 @@
1
- import os
2
- import sys
3
- import importlib
4
-
5
- from .iris_ipm import ipm
6
- from .iris_utils import update_dynalib_path
7
-
8
- # check for install dir in environment
9
- # environment to check is IRISINSTALLDIR
10
- # if not found, raise exception and exit
11
- # ISC_PACKAGE_INSTALLDIR - defined by default in Docker images
12
- installdir = os.environ.get('IRISINSTALLDIR') or os.environ.get('ISC_PACKAGE_INSTALLDIR')
13
- if installdir is None:
14
- raise Exception("""Cannot find InterSystems IRIS installation directory
15
- Please set IRISINSTALLDIR environment variable to the InterSystems IRIS installation directory""")
16
-
17
- __sysversion_info = sys.version_info
18
- __syspath = sys.path
19
- __osname = os.name
20
-
21
- # join the install dir with the bin directory
22
- __syspath.append(os.path.join(installdir, 'bin'))
23
- # also append lib/python
24
- __syspath.append(os.path.join(installdir, 'lib', 'python'))
25
-
26
- # update the dynalib path
27
- update_dynalib_path(os.path.join(installdir, 'bin'))
28
-
29
- # save working directory
30
- __ospath = os.getcwd()
31
-
32
- __irispythonint = None
33
-
34
- if __osname=='nt':
35
- if __sysversion_info.minor==9:
36
- __irispythonint = 'pythonint39'
37
- elif __sysversion_info.minor==10:
38
- __irispythonint = 'pythonint310'
39
- elif __sysversion_info.minor==11:
40
- __irispythonint = 'pythonint311'
41
- elif __sysversion_info.minor==12:
42
- __irispythonint = 'pythonint312'
43
- elif __sysversion_info.minor==13:
44
- __irispythonint = 'pythonint313'
45
- else:
46
- __irispythonint = 'pythonint'
47
-
48
- if __irispythonint is not None:
49
- # equivalent to from pythonint import *
50
- try:
51
- __irispythonintmodule = importlib.import_module(__irispythonint)
52
- except ImportError:
53
- __irispythonint = 'pythonint'
54
- __irispythonintmodule = importlib.import_module(__irispythonint)
55
- globals().update(vars(__irispythonintmodule))
56
-
57
- # restore working directory
58
- os.chdir(__ospath)
59
-
60
- # TODO: Figure out how to hide __syspath and __ospath from anyone that
61
- # imports iris. Tried __all__ but that only applies to this:
62
- # from iris import *
63
-
64
- #
65
- # End-of-file
66
- #