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,1671 @@
1
+ import re
2
+ import functools
3
+ import enum
4
+ import intersystems_iris._IRISList
5
+ import intersystems_iris.dbapi._DBAPI
6
+ import intersystems_iris.dbapi._Parameter
7
+ import intersystems_iris.dbapi.preparser._Token
8
+ import intersystems_iris.dbapi.preparser._TokenList
9
+ import intersystems_iris.dbapi.preparser._Scanner
10
+ from intersystems_iris.dbapi._Parameter import ParameterMode
11
+ from intersystems_iris.dbapi.preparser._Token import TOKEN
12
+ from intersystems_iris.dbapi.preparser._Scanner import ParseToken
13
+
14
+ class MultiValuesInsert(Exception):
15
+
16
+ def __init__(self, *args: object, query: str, rows: int, params=None) -> None:
17
+ super().__init__(*args)
18
+ self.query = query
19
+ self.rows = rows
20
+ self.params = params
21
+
22
+
23
+ # May want to move to its own file eventually
24
+ # SQL Statement Types
25
+ class StatementType(enum.IntEnum):
26
+ UPDATE = 0
27
+ QUERY = 1
28
+ CALL = 2
29
+ SYNC_COMMIT = 3
30
+ ASYNC_COMMIT = 4
31
+ STREAMS_OFF = 5
32
+ STREAMS_ON = 6
33
+ CALLWITHRESULT = 7
34
+ DDL_ALTER_DROP = 8
35
+ DDL_OTHER = 9
36
+ DIRECT_CALL_QUERY = 10
37
+ DIRECT_CALL_UPDATE = 11
38
+ PREPARED_CALL_QUERY = 12
39
+ PREPARED_CALL_UPDATE = 13
40
+ SQL_DIALECT = 14
41
+ STMT_USE = 15
42
+
43
+ class PreParseResult(object):
44
+ '''
45
+ A simple structure, returned by _PreParser.PreParse(), containing the parsed statement and statement type
46
+ '''
47
+ def __init__(self):
48
+ self.sResult = ""
49
+ self.p_eStmtType = None
50
+
51
+ class _PreParser(object):
52
+ """
53
+ This is the interface to the SQL PreParser. A string of SQL and a list of parameters ( bound parameters or empty ) is input.
54
+ The processed string is returned along with a count of parameters found and a classfication of the statement type
55
+ """
56
+ # Class properties
57
+ # Table for keyword lookups (used when tokenizing the statement)
58
+ s_KeywordTable = {}
59
+ s_KeywordTable["AND"] = TOKEN.OP
60
+ s_KeywordTable["BETWEEN"] = TOKEN.OP
61
+ s_KeywordTable["CHAR"] = TOKEN.DATATYPE
62
+ s_KeywordTable["CHARACTER"] = TOKEN.DATATYPE
63
+ s_KeywordTable["DEC"] = TOKEN.DATATYPE
64
+ s_KeywordTable["DECIMAL"] = TOKEN.DATATYPE
65
+ s_KeywordTable["FLOAT"] = TOKEN.DATATYPE
66
+ s_KeywordTable["IS"] = TOKEN.IS
67
+ s_KeywordTable["LIKE"] = TOKEN.OP
68
+ s_KeywordTable["LONGVARBINARY"] = TOKEN.DATATYPE
69
+ s_KeywordTable["LONGVARCHAR"] = TOKEN.DATATYPE
70
+ s_KeywordTable["NCHAR"] = TOKEN.DATATYPE
71
+ s_KeywordTable["NOT["] = TOKEN.OP
72
+ s_KeywordTable["NOT"] = TOKEN.NOT
73
+ s_KeywordTable["NULL"] = TOKEN.NULL
74
+ s_KeywordTable["NUMBER"] = TOKEN.DATATYPE
75
+ s_KeywordTable["NUMERIC"] = TOKEN.DATATYPE
76
+ s_KeywordTable["NVARCHAR"] = TOKEN.DATATYPE
77
+ s_KeywordTable["RAW"] = TOKEN.DATATYPE
78
+ s_KeywordTable["STARTSWITH"] = TOKEN.OP
79
+ s_KeywordTable["THEN"] = TOKEN.THEN
80
+ s_KeywordTable["ELSE"] = TOKEN.ELSE
81
+ s_KeywordTable["VARBINARY"] = TOKEN.DATATYPE
82
+ s_KeywordTable["VARCHAR"] = TOKEN.DATATYPE
83
+ s_KeywordTable["VARCHAR2"] = TOKEN.DATATYPE
84
+ s_KeywordTable["VARYING"] = TOKEN.DATATYPE
85
+ s_KeywordTable["_"] = TOKEN.NOT
86
+ s_KeywordTable["%SQLUPPER"] = TOKEN.STRFUNCTION
87
+ s_KeywordTable["%STRING"] = TOKEN.STRFUNCTION
88
+ s_KeywordTable["%SQLSTRING"] = TOKEN.STRFUNCTION
89
+ s_KeywordTable["%TRUNCATE"] = TOKEN.STRFUNCTION
90
+ s_KeywordTable["TRUNCATE"] = TOKEN.STRFUNCTION
91
+
92
+ # Table for statement type lookups
93
+ s_StatementTable = {}
94
+ s_StatementTable["ALTER"] = StatementType.DDL_ALTER_DROP
95
+ s_StatementTable["CREATE"] = StatementType.DDL_OTHER
96
+ s_StatementTable["DROP"] = StatementType.DDL_ALTER_DROP
97
+ s_StatementTable["GRANT"] = StatementType.DDL_OTHER
98
+ s_StatementTable["REVOKE"] = StatementType.DDL_OTHER
99
+ s_StatementTable["%CHECKPRIV"] = StatementType.DDL_OTHER
100
+ s_StatementTable["TRAIN"] = StatementType.DDL_OTHER
101
+ s_StatementTable["VALIDATE"] = StatementType.DDL_OTHER
102
+ s_StatementTable["TUNE"] = StatementType.DDL_OTHER
103
+ s_StatementTable["VALIDATE"] = StatementType.DDL_OTHER
104
+
105
+ s_StatementTable["USE"] = StatementType.STMT_USE
106
+ s_StatementTable["EXPLAIN"] = StatementType.CALLWITHRESULT
107
+
108
+ # Table for common statement type lookups (SELECT,DELETE,UPDATE,INSERT)
109
+ s_ParsedStatements = {}
110
+ s_ParsedStatements["SELECT"] = StatementType.QUERY
111
+ s_ParsedStatements["INSERT"] = StatementType.UPDATE
112
+ s_ParsedStatements["DELETE"] = StatementType.UPDATE
113
+ s_ParsedStatements["UPDATE"] = StatementType.UPDATE
114
+
115
+ # Table for statements to cache, beyond those in s_ParsedStatements
116
+ # TODO: change this to be a set
117
+ # Since the server now tells us whether to cache, this may be superfluous
118
+ s_TransactionStatements = {}
119
+ s_TransactionStatements["COMMIT"] = True
120
+ s_TransactionStatements["ROLLBACK"] = True
121
+ s_TransactionStatements["START"] = True
122
+ s_TransactionStatements["%INTRANSACTION"] = True
123
+ s_TransactionStatements["%INTRANS"] = True
124
+ s_TransactionStatements["%BEGTRANS"] = True
125
+
126
+ # keywords for replacing parameters
127
+ s_replaceparm = " SELECT TOP WHERE ON AND OR NOT BETWEEN %STARTSWITH LIKE CASE WHEN ELSE THEN"
128
+
129
+ # keywords that should be output all upper case after preparsing
130
+ s_ReservedKeywords = set()
131
+ s_ReservedKeywords.add("%AFTERHAVING")
132
+ s_ReservedKeywords.add("%ALLINDEX")
133
+ s_ReservedKeywords.add("%ALPHAUP")
134
+ s_ReservedKeywords.add("%ALTER")
135
+ s_ReservedKeywords.add("%BEGTRANS")
136
+ s_ReservedKeywords.add("%CHECKPRIV")
137
+ s_ReservedKeywords.add("%CLASSNAME")
138
+ s_ReservedKeywords.add("%CLASSPARAMETER")
139
+ s_ReservedKeywords.add("%DBUGFULL")
140
+ s_ReservedKeywords.add("%DELDATA")
141
+ s_ReservedKeywords.add("%DESCRIPTION")
142
+ s_ReservedKeywords.add("%EXACT")
143
+ s_ReservedKeywords.add("%EXTERNAL")
144
+ s_ReservedKeywords.add("%FILE")
145
+ s_ReservedKeywords.add("%FIRSTTABLE")
146
+ s_ReservedKeywords.add("%FLATTEN")
147
+ s_ReservedKeywords.add("%FOREACH")
148
+ s_ReservedKeywords.add("%FULL")
149
+ s_ReservedKeywords.add("%ID")
150
+ s_ReservedKeywords.add("%IDADDED")
151
+ s_ReservedKeywords.add("%IGNOREINDEX")
152
+ s_ReservedKeywords.add("%IGNOREINDICES")
153
+ s_ReservedKeywords.add("%INLIST")
154
+ s_ReservedKeywords.add("%INORDER")
155
+ s_ReservedKeywords.add("%INTERNAL")
156
+ s_ReservedKeywords.add("%INTEXT")
157
+ s_ReservedKeywords.add("%INTRANS")
158
+ s_ReservedKeywords.add("%INTRANSACTION")
159
+ s_ReservedKeywords.add("%KEY")
160
+ s_ReservedKeywords.add("%MATCHES")
161
+ s_ReservedKeywords.add("%MCODE")
162
+ s_ReservedKeywords.add("%MERGE")
163
+ s_ReservedKeywords.add("%MINUS")
164
+ s_ReservedKeywords.add("%MVR")
165
+ s_ReservedKeywords.add("%NOCHECK")
166
+ s_ReservedKeywords.add("%NODELDATA")
167
+ s_ReservedKeywords.add("%NOFLATTEN")
168
+ s_ReservedKeywords.add("%NOFPLAN")
169
+ s_ReservedKeywords.add("%NOINDEX")
170
+ s_ReservedKeywords.add("%NOLOCK")
171
+ s_ReservedKeywords.add("%NOMERGE")
172
+ s_ReservedKeywords.add("%NOPARALLEL")
173
+ s_ReservedKeywords.add("%NOREDUCE")
174
+ s_ReservedKeywords.add("%NORUNTIME")
175
+ s_ReservedKeywords.add("%NOSVSO")
176
+ s_ReservedKeywords.add("%NOTOPOPT")
177
+ s_ReservedKeywords.add("%NOTRIGGER")
178
+ s_ReservedKeywords.add("%NOUNIONOROPT")
179
+ s_ReservedKeywords.add("%NUMROWS")
180
+ s_ReservedKeywords.add("%ODBCIN")
181
+ s_ReservedKeywords.add("%ODBCOUT")
182
+ s_ReservedKeywords.add("%PARALLEL")
183
+ s_ReservedKeywords.add("%PLUS")
184
+ s_ReservedKeywords.add("%PROFILE")
185
+ s_ReservedKeywords.add("%PROFILE_ALL")
186
+ s_ReservedKeywords.add("%PUBLICROWID")
187
+ s_ReservedKeywords.add("%ROUTINE")
188
+ s_ReservedKeywords.add("%ROWCOUNT")
189
+ s_ReservedKeywords.add("%RUNTIMEIN")
190
+ s_ReservedKeywords.add("%RUNTIMEOUT")
191
+ s_ReservedKeywords.add("%STARTSWITH")
192
+ s_ReservedKeywords.add("%STARTTABLE")
193
+ s_ReservedKeywords.add("%SQLSTRING")
194
+ s_ReservedKeywords.add("%SQLUPPER")
195
+ s_ReservedKeywords.add("%STRING")
196
+ s_ReservedKeywords.add("%TABLENAME")
197
+ s_ReservedKeywords.add("%TRUNCATE")
198
+ s_ReservedKeywords.add("%UPPER")
199
+ s_ReservedKeywords.add("%VALUE")
200
+ s_ReservedKeywords.add("%VID")
201
+ s_ReservedKeywords.add("ABSOLUTE")
202
+ s_ReservedKeywords.add("ADD")
203
+ s_ReservedKeywords.add("ALL")
204
+ s_ReservedKeywords.add("ALLOCATE")
205
+ s_ReservedKeywords.add("ALTER")
206
+ s_ReservedKeywords.add("AND")
207
+ s_ReservedKeywords.add("ANY")
208
+ s_ReservedKeywords.add("ARE")
209
+ s_ReservedKeywords.add("AS")
210
+ s_ReservedKeywords.add("ASC")
211
+ s_ReservedKeywords.add("ASSERTION")
212
+ s_ReservedKeywords.add("AT")
213
+ s_ReservedKeywords.add("AUTHORIZATION")
214
+ s_ReservedKeywords.add("AVG")
215
+ s_ReservedKeywords.add("BEGIN")
216
+ s_ReservedKeywords.add("BETWEEN")
217
+ s_ReservedKeywords.add("BIT")
218
+ s_ReservedKeywords.add("BIT_LENGTH")
219
+ s_ReservedKeywords.add("BOTH")
220
+ s_ReservedKeywords.add("BY")
221
+ s_ReservedKeywords.add("CASCADE")
222
+ s_ReservedKeywords.add("CASE")
223
+ s_ReservedKeywords.add("CAST")
224
+ s_ReservedKeywords.add("CHAR")
225
+ s_ReservedKeywords.add("CHARACTER")
226
+ s_ReservedKeywords.add("CHARACTER_LENGTH")
227
+ s_ReservedKeywords.add("CHAR_LENGTH")
228
+ s_ReservedKeywords.add("CHECK")
229
+ s_ReservedKeywords.add("CLOSE")
230
+ s_ReservedKeywords.add("COALESCE")
231
+ s_ReservedKeywords.add("COLLATE")
232
+ s_ReservedKeywords.add("COMMIT")
233
+ s_ReservedKeywords.add("CONNECT")
234
+ s_ReservedKeywords.add("CONNECTION")
235
+ s_ReservedKeywords.add("CONSTRAINT")
236
+ s_ReservedKeywords.add("CONSTRAINTS")
237
+ s_ReservedKeywords.add("CONTINUE")
238
+ s_ReservedKeywords.add("CONVERT")
239
+ s_ReservedKeywords.add("CORRESPONDING")
240
+ s_ReservedKeywords.add("COUNT")
241
+ s_ReservedKeywords.add("CREATE")
242
+ s_ReservedKeywords.add("CROSS")
243
+ s_ReservedKeywords.add("CURRENT")
244
+ s_ReservedKeywords.add("CURRENT_DATE")
245
+ s_ReservedKeywords.add("CURRENT_TIME")
246
+ s_ReservedKeywords.add("CURRENT_TIMESTAMP")
247
+ s_ReservedKeywords.add("CURRENT_USER")
248
+ s_ReservedKeywords.add("CURSOR")
249
+ s_ReservedKeywords.add("DATE")
250
+ s_ReservedKeywords.add("DEALLOCATE")
251
+ s_ReservedKeywords.add("DEC")
252
+ s_ReservedKeywords.add("DECIMAL")
253
+ s_ReservedKeywords.add("DECLARE")
254
+ s_ReservedKeywords.add("DEFAULT")
255
+ s_ReservedKeywords.add("DEFERRABLE")
256
+ s_ReservedKeywords.add("DEFERRED")
257
+ s_ReservedKeywords.add("DELETE")
258
+ s_ReservedKeywords.add("DESC")
259
+ s_ReservedKeywords.add("DESCRIBE")
260
+ s_ReservedKeywords.add("DESCRIPTOR")
261
+ s_ReservedKeywords.add("DIAGNOSTICS")
262
+ s_ReservedKeywords.add("DISCONNECT")
263
+ s_ReservedKeywords.add("DISTINCT")
264
+ s_ReservedKeywords.add("DOMAIN")
265
+ s_ReservedKeywords.add("DOUBLE")
266
+ s_ReservedKeywords.add("DROP")
267
+ s_ReservedKeywords.add("ELSE")
268
+ s_ReservedKeywords.add("END")
269
+ s_ReservedKeywords.add("ENDEXEC")
270
+ s_ReservedKeywords.add("ESCAPE")
271
+ s_ReservedKeywords.add("EXCEPT")
272
+ s_ReservedKeywords.add("EXCEPTION")
273
+ s_ReservedKeywords.add("EXEC")
274
+ s_ReservedKeywords.add("EXECUTE")
275
+ s_ReservedKeywords.add("EXISTS")
276
+ s_ReservedKeywords.add("EXTERNAL")
277
+ s_ReservedKeywords.add("EXTRACT")
278
+ s_ReservedKeywords.add("FALSE")
279
+ s_ReservedKeywords.add("FETCH")
280
+ s_ReservedKeywords.add("FIRST")
281
+ s_ReservedKeywords.add("FLOAT")
282
+ s_ReservedKeywords.add("FOR")
283
+ s_ReservedKeywords.add("FOREIGN")
284
+ s_ReservedKeywords.add("FOUND")
285
+ s_ReservedKeywords.add("FROM")
286
+ s_ReservedKeywords.add("FULL")
287
+ s_ReservedKeywords.add("GET")
288
+ s_ReservedKeywords.add("GLOBAL")
289
+ s_ReservedKeywords.add("GO")
290
+ s_ReservedKeywords.add("GOTO")
291
+ s_ReservedKeywords.add("GRANT")
292
+ s_ReservedKeywords.add("GROUP")
293
+ s_ReservedKeywords.add("HAVING")
294
+ s_ReservedKeywords.add("HOUR")
295
+ s_ReservedKeywords.add("IDENTITY")
296
+ s_ReservedKeywords.add("IMMEDIATE")
297
+ s_ReservedKeywords.add("IN")
298
+ s_ReservedKeywords.add("INDICATOR")
299
+ s_ReservedKeywords.add("INITIALLY")
300
+ s_ReservedKeywords.add("INNER")
301
+ s_ReservedKeywords.add("INPUT")
302
+ s_ReservedKeywords.add("INSENSITIVE")
303
+ s_ReservedKeywords.add("INSERT")
304
+ s_ReservedKeywords.add("INT")
305
+ s_ReservedKeywords.add("INTEGER")
306
+ s_ReservedKeywords.add("INTERSECT")
307
+ s_ReservedKeywords.add("INTERVAL")
308
+ s_ReservedKeywords.add("INTO")
309
+ s_ReservedKeywords.add("IS")
310
+ s_ReservedKeywords.add("ISOLATION")
311
+ s_ReservedKeywords.add("JOIN")
312
+ s_ReservedKeywords.add("LANGUAGE")
313
+ s_ReservedKeywords.add("LAST")
314
+ s_ReservedKeywords.add("LEADING")
315
+ s_ReservedKeywords.add("LEFT")
316
+ s_ReservedKeywords.add("LEVEL")
317
+ s_ReservedKeywords.add("LIKE")
318
+ s_ReservedKeywords.add("LOCAL")
319
+ s_ReservedKeywords.add("LOWER")
320
+ s_ReservedKeywords.add("MATCH")
321
+ s_ReservedKeywords.add("MAX")
322
+ s_ReservedKeywords.add("MIN")
323
+ s_ReservedKeywords.add("MINUTE")
324
+ s_ReservedKeywords.add("MODULE")
325
+ s_ReservedKeywords.add("NAMES")
326
+ s_ReservedKeywords.add("NATIONAL")
327
+ s_ReservedKeywords.add("NATURAL")
328
+ s_ReservedKeywords.add("NCHAR")
329
+ s_ReservedKeywords.add("NEXT")
330
+ s_ReservedKeywords.add("NO")
331
+ s_ReservedKeywords.add("NOT")
332
+ s_ReservedKeywords.add("NULL")
333
+ s_ReservedKeywords.add("NULLIF")
334
+ s_ReservedKeywords.add("NUMERIC")
335
+ s_ReservedKeywords.add("OCTET_LENGTH")
336
+ s_ReservedKeywords.add("OF")
337
+ s_ReservedKeywords.add("ON")
338
+ s_ReservedKeywords.add("ONLY")
339
+ s_ReservedKeywords.add("OPEN")
340
+ s_ReservedKeywords.add("OPTION")
341
+ s_ReservedKeywords.add("OR")
342
+ s_ReservedKeywords.add("OUTER")
343
+ s_ReservedKeywords.add("OUTPUT")
344
+ s_ReservedKeywords.add("OVERLAPS")
345
+ s_ReservedKeywords.add("PAD")
346
+ s_ReservedKeywords.add("PARTIAL")
347
+ s_ReservedKeywords.add("PREPARE")
348
+ s_ReservedKeywords.add("PRESERVE")
349
+ s_ReservedKeywords.add("PRIMARY")
350
+ s_ReservedKeywords.add("PRIOR")
351
+ s_ReservedKeywords.add("PRIVILEGES")
352
+ s_ReservedKeywords.add("PROCEDURE")
353
+ s_ReservedKeywords.add("PUBLIC")
354
+ s_ReservedKeywords.add("READ")
355
+ s_ReservedKeywords.add("REAL")
356
+ s_ReservedKeywords.add("REFERENCES")
357
+ s_ReservedKeywords.add("RELATIVE")
358
+ s_ReservedKeywords.add("RESTRICT")
359
+ s_ReservedKeywords.add("REVOKE")
360
+ s_ReservedKeywords.add("RIGHT")
361
+ s_ReservedKeywords.add("ROLE")
362
+ s_ReservedKeywords.add("ROLLBACK")
363
+ s_ReservedKeywords.add("ROWS")
364
+ s_ReservedKeywords.add("SCHEMA")
365
+ s_ReservedKeywords.add("SCROLL")
366
+ s_ReservedKeywords.add("SECOND")
367
+ s_ReservedKeywords.add("SECTION")
368
+ s_ReservedKeywords.add("SELECT")
369
+ s_ReservedKeywords.add("SESSION_USER")
370
+ s_ReservedKeywords.add("SET")
371
+ s_ReservedKeywords.add("SHARD")
372
+ s_ReservedKeywords.add("SMALLINT")
373
+ s_ReservedKeywords.add("SOME")
374
+ s_ReservedKeywords.add("SPACE")
375
+ s_ReservedKeywords.add("SQLERROR")
376
+ s_ReservedKeywords.add("SQLSTATE")
377
+ s_ReservedKeywords.add("STATISTICS")
378
+ s_ReservedKeywords.add("SUBSTRING")
379
+ s_ReservedKeywords.add("SUM")
380
+ s_ReservedKeywords.add("SYSDATE")
381
+ s_ReservedKeywords.add("SYSTEM_USER")
382
+ s_ReservedKeywords.add("TABLE")
383
+ s_ReservedKeywords.add("TEMPORARY")
384
+ s_ReservedKeywords.add("THEN")
385
+ s_ReservedKeywords.add("TIME")
386
+ s_ReservedKeywords.add("TIMEZONE_HOUR")
387
+ s_ReservedKeywords.add("TIMEZONE_MINUTE")
388
+ s_ReservedKeywords.add("TO")
389
+ s_ReservedKeywords.add("TOP")
390
+ s_ReservedKeywords.add("TRAILING")
391
+ s_ReservedKeywords.add("TRANSACTION")
392
+ s_ReservedKeywords.add("TRIM")
393
+ s_ReservedKeywords.add("TRUE")
394
+ s_ReservedKeywords.add("UNION")
395
+ s_ReservedKeywords.add("UNIQUE")
396
+ s_ReservedKeywords.add("UPDATE")
397
+ s_ReservedKeywords.add("UPPER")
398
+ s_ReservedKeywords.add("USER")
399
+ s_ReservedKeywords.add("USING")
400
+ s_ReservedKeywords.add("VALUES")
401
+ s_ReservedKeywords.add("VARCHAR")
402
+ s_ReservedKeywords.add("VARYING")
403
+ s_ReservedKeywords.add("WHEN")
404
+ s_ReservedKeywords.add("WHENEVER")
405
+ s_ReservedKeywords.add("WHERE")
406
+ s_ReservedKeywords.add("WITH")
407
+ s_ReservedKeywords.add("WORK")
408
+ s_ReservedKeywords.add("WRITE")
409
+
410
+ # Supported SQL Dialects
411
+ SQL_DIALECT_DEFAULT = 0
412
+ SQL_DIALECT_MSSQL = 1
413
+ SQL_DIALECT_SYBASE = 2
414
+
415
+ # methods
416
+ def CacheOnServerGet(self):
417
+ return self.m_CacheOnServer
418
+
419
+ def CacheOnServerSet(self, b):
420
+ b = bool(b)
421
+
422
+ self.m_CacheOnServer = b
423
+
424
+ def ParamInfoGet(self):
425
+ return self.m_ParamInfo
426
+
427
+ def ParamInfoSet(self, s):
428
+ if not isinstance(type(s), intersystems_iris._ListWriter._ListWriter):
429
+ raise TypeError("s must be a _ListWriter")
430
+
431
+ self.m_ParamInfo = s
432
+
433
+ # Build a PreParser
434
+ def __init__(self, p_bDelimitedIdentifiers = False, addRID = 0, embedded = False):
435
+ p_bDelimitedIdentifiers = bool(p_bDelimitedIdentifiers)
436
+ try:
437
+ addRID = int(addRID)
438
+ except (TypeError, ValueError):
439
+ raise TypeError("addRID must be an interger")
440
+
441
+ self.m_addRowID = addRID
442
+ self.m_ExecParamCount = 0
443
+ self.m_ParamInfo = intersystems_iris._IRISList._IRISList()
444
+
445
+ # flags for delimited identifier use
446
+ self.m_bDelimitedIdentifiers = p_bDelimitedIdentifiers
447
+ self.m_bBracketSubstitution = False
448
+
449
+ # flag for when statements are cached on the server
450
+ # potentially irrelevant now because server tells us directly whether it cached the statement
451
+ self.CacheOnServerSet(False)
452
+
453
+ # List for tokenizer
454
+ self.m_Tokens = None
455
+
456
+ # The source scanner
457
+ self.m_Scanner = None
458
+
459
+ # flag for when Named Parameters are used
460
+ self.hasNamedParameters = False
461
+
462
+ # use to pass UndefinedCount value from methods
463
+ self.m_nUndefinedCount = 0
464
+
465
+ self.embedded = embedded
466
+
467
+ # Preparse an SQL string returning output statement, parameters, parameter count and statement type
468
+ def PreParse(self, query, p_Parameters):
469
+
470
+ t_query = query
471
+ while True:
472
+ # First tokenize the input
473
+ self.Tokenize(t_query)
474
+ # Convert WITH Clause, can be recursive
475
+ found_with, t_query = self.With(t_query)
476
+ if not found_with:
477
+ break
478
+
479
+ found_insert, t_query = self.InsertMultiValues(t_query)
480
+ if found_insert:
481
+ self.Tokenize(t_query)
482
+
483
+ # Resolve the tokens and determine output
484
+ return self.Resolve(t_query, p_Parameters)
485
+
486
+ def With(self, query):
487
+ try:
488
+ found = False
489
+ new_query = ''
490
+ with_statements = {}
491
+
492
+ def _query(find_end_paren=True):
493
+ sub_query = ''
494
+ open_parens = 0
495
+ while tokens.MoveNext():
496
+ token = tokens.Current()
497
+
498
+ if token.TokenType is TOKEN.OPEN_PAREN:
499
+ open_parens += 1
500
+ elif token.TokenType is TOKEN.CLOSE_PAREN:
501
+ open_parens -= 1
502
+
503
+ sub_query += token.Lexeme
504
+ sub_query += ' '
505
+
506
+ if token.TokenType is TOKEN.ID and (token.UpperEquals('FROM') or token.UpperEquals('JOIN')):
507
+ assert tokens.MoveNext() and tokens.Current().TokenType is TOKEN.ID
508
+ table_name = tokens.Current().Lexeme
509
+ table_name_upper = tokens.Current().UpperLexeme
510
+ if table_name_upper in with_statements:
511
+ sub_query += with_statements[table_name_upper]
512
+ sub_query += ' AS '
513
+ sub_query += table_name
514
+ sub_query += ' '
515
+
516
+ if find_end_paren and open_parens == 0:
517
+ break
518
+ return sub_query
519
+
520
+ tokens = self.m_Tokens.GetEnumerator()
521
+ while tokens.MoveNext():
522
+ token = tokens.Current()
523
+
524
+ if token.TokenType is TOKEN.ID and token.UpperEquals("WITH"):
525
+ found = True
526
+ break
527
+ else:
528
+ new_query += token.Lexeme
529
+ new_query += ' '
530
+
531
+ if not found:
532
+ return False, query
533
+
534
+ while True:
535
+ assert tokens.MoveNext() and tokens.Current().TokenType is TOKEN.ID
536
+ with_name = tokens.Current().UpperLexeme
537
+ assert tokens.MoveNext() and tokens.Current().TokenType is TOKEN.ID and tokens.Current().UpperContains('AS')
538
+ assert tokens.MoveNext() and tokens.Current().TokenType is TOKEN.OPEN_PAREN
539
+
540
+ tokens.MovePrevious()
541
+ with_statements[with_name] = _query()
542
+
543
+ if not tokens.MoveNext() or tokens.Current().TokenType is not TOKEN.COMMA:
544
+ tokens.MovePrevious()
545
+ break
546
+
547
+ assert tokens.MoveNext()
548
+ tokens.MovePrevious()
549
+ new_query += _query(False)
550
+
551
+ return found, new_query
552
+ except:
553
+ return False, query
554
+
555
+ def InsertMultiValues(self, query):
556
+ new_query = ''
557
+ values_list = []
558
+
559
+ tokens = self.m_Tokens.GetEnumerator()
560
+ while tokens.MoveNext() and not tokens.Current().UpperEquals("INSERT"):
561
+ new_query += tokens.Current().Lexeme + ' '
562
+ if not tokens.MoveNext() or not tokens.Current().UpperEquals("INTO"):
563
+ return False, query
564
+ new_query += 'INSERT INTO '
565
+ while tokens.MoveNext() and not tokens.Current().UpperEquals("VALUES"):
566
+ new_query += tokens.Current().Lexeme + ' '
567
+
568
+ values = ''
569
+ params = []
570
+ while tokens.MoveNext():
571
+ assert tokens.Current().TokenType is TOKEN.OPEN_PAREN
572
+ open_parens = 1
573
+ while tokens.MoveNext() or open_parens > 0:
574
+ token = tokens.Current()
575
+ if token.TokenType is TOKEN.OPEN_PAREN:
576
+ open_parens += 1
577
+ elif token.TokenType is TOKEN.CLOSE_PAREN:
578
+ open_parens -= 1
579
+ if open_parens == 0:
580
+ break
581
+ if token.TokenType is TOKEN.CONSTANT:
582
+ values += '?'
583
+ params += [token.Lexeme]
584
+ else:
585
+ values += token.Lexeme
586
+ values += ' '
587
+ values_list.append(values)
588
+ values = ''
589
+ if not tokens.MoveNext() or tokens.Current().TokenType is not TOKEN.COMMA:
590
+ break
591
+
592
+ if len(values_list) <= 1:
593
+ return False, query
594
+
595
+ new_query += f" VALUES ({values_list[0]})"
596
+ raise MultiValuesInsert(query=new_query, rows=len(values_list), params=params)
597
+
598
+ # Parse a statement
599
+ def Tokenize(self, p_strInput):
600
+ # Get a scanner on the sql string
601
+ self.m_Scanner = intersystems_iris.dbapi.preparser._Scanner._Scanner(p_strInput)
602
+ # Create a new token list
603
+ self.m_Tokens = intersystems_iris.dbapi.preparser._TokenList._TokenList()
604
+ # Scan the input string and break into tokens
605
+ tokenize_switcher = {
606
+ ParseToken.tokEOS: self.Tokenize_eos,
607
+ ParseToken.tokDOT: self.Tokenize_dot,
608
+ ParseToken.tokDIGIT: self.Tokenize_digit,
609
+ ParseToken.tokMINUS: self.Tokenize_minus,
610
+ ParseToken.tokPLUS: self.Tokenize_plus,
611
+ ParseToken.tokLBRACK: self.Tokenize_lbrack,
612
+ ParseToken.tokDQUOTE: self.Tokenize_quote,
613
+ ParseToken.tokSQUOTE: self.Tokenize_quote,
614
+ ParseToken.tokSLASH: self.Tokenize_slash,
615
+ ParseToken.tokQUEST: functools.partial(self.Tokenize_single, token = TOKEN.QUESTION_MARK, char = "?"),
616
+ ParseToken.tokATSIGN: self.Tokenize_atsign,
617
+ ParseToken.tokLPARN: functools.partial(self.Tokenize_single, token = TOKEN.OPEN_PAREN, char = "("),
618
+ ParseToken.tokRPARN: functools.partial(self.Tokenize_single, token = TOKEN.CLOSE_PAREN, char = ")"),
619
+ ParseToken.tokCOMMA: functools.partial(self.Tokenize_single, token = TOKEN.COMMA, char = ","),
620
+ ParseToken.tokCOLON: self.Tokenize_colon,
621
+ ParseToken.tokLETTER: self.Tokenize_identifier,
622
+ ParseToken.tokPERCENT: self.Tokenize_identifier,
623
+ ParseToken.tokDOLLAR: self.Tokenize_identifier,
624
+ ParseToken.tokUSCORE: self.Tokenize_identifier,
625
+ ParseToken.tokPOUND: self.Tokenize_identifier,
626
+ ParseToken.tokLESS: functools.partial(self.Tokenize_op, check_tokens = [ParseToken.tokEQUAL, ParseToken.tokGREAT]),
627
+ ParseToken.tokEXCLA: self.Tokenize_op,
628
+ ParseToken.tokGREAT: self.Tokenize_op,
629
+ ParseToken.tokASTER: self.Tokenize_op,
630
+ ParseToken.tokEQUAL: functools.partial(self.Tokenize_op, check_tokens = [ParseToken.tokASTER]),
631
+ ParseToken.tokVBAR: self.Tokenize_vbar,
632
+ ParseToken.tokLBRACE: self.Tokenize_lbrace
633
+ }
634
+ while self.m_Scanner.CurrentTokenGet() != ParseToken.tokEOS:
635
+ self.m_Scanner.SkipWhitespace()
636
+
637
+ tokenize_func = tokenize_switcher.get(self.m_Scanner.CurrentTokenGet(), self.Tokenize_default)
638
+ tokenize_func()
639
+
640
+ # generic function for when a token consists of a single character
641
+ def Tokenize_single(self, token, char):
642
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(token, char))
643
+ # Skip this character
644
+ self.m_Scanner.NextToken()
645
+
646
+ # default behavior for an unknown character or ParseToken
647
+ def Tokenize_default(self, token = TOKEN.UNKNOWN):
648
+ self.m_Scanner.BeginLexeme()
649
+ self.m_Scanner.NextToken() # One character unknown
650
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(token, self.m_Scanner.EndLexeme(), self.m_Scanner.EndUpperLexeme()))
651
+
652
+ # end of source, do nothing
653
+ def Tokenize_eos(self):
654
+ pass
655
+
656
+ # if dot is part of a decimal, parse a number, otherwise default behavior
657
+ def Tokenize_dot(self):
658
+ if ParseToken.tokDIGIT != self.m_Scanner.PeekNextToken():
659
+ self.Tokenize_default()
660
+ else:
661
+ self.Tokenize_digit()
662
+
663
+ # either the beginning of hex data, or a number
664
+ def Tokenize_digit(self):
665
+ (t_strNumber, goodParse) = self.m_Scanner.Hex()
666
+ if goodParse:
667
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(TOKEN.HEX, t_strNumber))
668
+ return
669
+
670
+ (t_strNumber, goodParse) = self.m_Scanner.Number()
671
+ if not goodParse:
672
+ raise Exception("Invalid Numeric Constant")
673
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(TOKEN.CONSTANT, t_strNumber))
674
+
675
+ def Tokenize_minus(self):
676
+ nextToken = self.m_Scanner.PeekNextToken()
677
+ if nextToken == ParseToken.tokMINUS:
678
+ # Continuation sequence, skip to next line
679
+ self.m_Scanner.Skip(2)
680
+ self.m_Scanner.BeginLexeme()
681
+ self.m_Scanner.SkipToEndOfLine() # Skip '--' to end of line
682
+ # DVU m_Tokens.Append(new _Token(TOKEN.UNKNOWN, "/*" + m_Scanner.EndLexeme() + "*/"))
683
+ return
684
+ elif nextToken == ParseToken.tokGREAT:
685
+ # -> operator
686
+ self.m_Scanner.BeginLexeme()
687
+ self.m_Scanner.Skip(2) # Skip '->'
688
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(TOKEN.OP, self.m_Scanner.EndLexeme()))
689
+ return
690
+ self.Tokenize_plus("-")
691
+
692
+ def Tokenize_plus(self, op_char = "+"):
693
+ # RULE: Per Aviel, Preparser.txt. A numeric constant may include a preceding "+" or "-" ,
694
+ # but only if the token before the +/- is an OP or LPAR, otherwise the +/- might be
695
+ # a monadic operator and should be considered an OP.
696
+ t_eToken = self.m_Tokens.Last().GetValue().TokenTypeGet() if self.m_Tokens.Last() is not None else TOKEN.UNKNOWN
697
+ if t_eToken in [TOKEN.OP, TOKEN.OPEN_PAREN, TOKEN.COMMA] and (self.m_Scanner.PeekNextToken == ParseToken.tokDIGIT or (self.m_Scanner.PeekNextToken() == ParseToken.tokDOT and self.m_Scanner.PeekNextNextToken() == ParseToken.tokDIGIT)):
698
+ # Scan in number
699
+ (t_strNumber, goodParse) = self.m_Scanner.Number()
700
+ if not goodParse:
701
+ # TO DO: Replace with ParseException
702
+ raise Exception("Invalid Numeric Constant")
703
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(TOKEN.CONSTANT, t_strNumber))
704
+ else:
705
+ self.Tokenize_single(TOKEN.OP, op_char)
706
+
707
+ def Tokenize_lbrack(self):
708
+ if self.m_bBracketSubstitution:
709
+ if not self.m_bDelimitedIdentifiers:
710
+ raise Exception("Delimited identifiers must be enabled on the server to support brackets")
711
+ (t_strString, t_eToken) = self.m_Scanner.ParseBrackets(self.m_bDelimitedIdentifiers)
712
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(t_eToken, t_strString))
713
+ return
714
+ self.Tokenize_default()
715
+
716
+ # quotes indicate a string
717
+ def Tokenize_quote(self):
718
+ (t_strString, t_eToken) = self.m_Scanner.String(self.m_bDelimitedIdentifiers)
719
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(t_eToken, t_strString))
720
+
721
+ def Tokenize_slash(self):
722
+ if self.m_Scanner.PeekNextToken() == ParseToken.tokASTER:
723
+ # scan in the comment
724
+ self.m_Scanner.BeginLexeme()
725
+ # Skip '/' '*'
726
+ self.m_Scanner.Skip(2)
727
+ # Scan in the comment, returns true if successful scan
728
+ if not self.m_Scanner.Comment():
729
+ # Ran off end of statement
730
+ # TO DO: Replace with ParseException?
731
+ raise Exception("Unexpected End-Of-Statement")
732
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(TOKEN.UNKNOWN, self.m_Scanner.EndLexeme(), self.m_Scanner.EndUpperLexeme()))
733
+ else:
734
+ self.Tokenize_default(TOKEN.OP) # '/' operator
735
+
736
+ # '@' used for named parameters
737
+ def Tokenize_atsign(self):
738
+ self.m_Scanner.NextToken()
739
+ if self.m_Scanner.CurrentTokenGet() == ParseToken.tokDIGIT:
740
+ raise Exception(("Parameter Name error, First value cannot be a digit: " + self.m_Scanner.CurrentChar()))
741
+ t_strID = self.m_Scanner.Identifier()
742
+ if t_strID == "":
743
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(TOKEN.QUESTION_MARK, "?"))
744
+ else:
745
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(TOKEN.ATSIGN, "@" + t_strID))
746
+
747
+ # ':' indicates variables
748
+ def Tokenize_colon(self):
749
+ # Skip ':'
750
+ self.m_Scanner.NextToken()
751
+ # Scan in a variable
752
+ t_strVariable = self.m_Scanner.Variable()
753
+ t_strVariable = ":" + t_strVariable
754
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(TOKEN.VAR, t_strVariable))
755
+
756
+ def Tokenize_identifier(self):
757
+ # Initially, the token is an ID
758
+ t_eToken = TOKEN.ID
759
+ # Scan in an identifier
760
+ t_strID = self.m_Scanner.Identifier()
761
+ # Get an uppercase version for lookups
762
+ t_strIDUpper = self.m_Scanner.EndUpperLexeme()
763
+ # Do a table lookup to identify token
764
+ if t_strIDUpper in self.s_KeywordTable:
765
+ # Found it, replace ID with specific type
766
+ t_eToken = self.s_KeywordTable[t_strIDUpper]
767
+ if (t_eToken == TOKEN.NOT):
768
+ t_strID = self.m_Scanner.checkForNotPredicates()
769
+ t_strIDUpper = t_strID.upper()
770
+ if t_strID == '%s':
771
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(TOKEN.QUESTION_MARK, "?"))
772
+ else:
773
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(t_eToken, t_strID, t_strIDUpper))
774
+
775
+ # used for various operators
776
+ def Tokenize_op(self, check_tokens = [ParseToken.tokEQUAL]):
777
+ self.m_Scanner.BeginLexeme()
778
+ if self.m_Scanner.PeekNextToken() in check_tokens:
779
+ # Check for composite operators (e.g. <=, >=, !=, etc.)
780
+ self.m_Scanner.NextToken()
781
+ self.m_Scanner.NextToken()
782
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(TOKEN.OP, self.m_Scanner.EndLexeme()))
783
+
784
+ # either || operator, or unknown
785
+ def Tokenize_vbar(self):
786
+ self.m_Scanner.BeginLexeme()
787
+ t_eToken = TOKEN.OP
788
+ if self.m_Scanner.PeekNextToken() == ParseToken.tokVBAR:
789
+ self.m_Scanner.Skip(2)
790
+ else:
791
+ self.m_Scanner.NextToken()
792
+ t_eToken = TOKEN.UNKNOWN
793
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(t_eToken, self.m_Scanner.EndLexeme(), self.m_Scanner.EndUpperLexeme()))
794
+
795
+ def Tokenize_lbrace(self):
796
+ self.m_Scanner.NextToken() # Skip '{'
797
+ # Create a checkpoint
798
+ t_CP = self.m_Scanner.CreateCheckPoint()
799
+ self.m_Scanner.SkipWhitespace()
800
+ # Scan in a potential keyowrd
801
+ t_strKeyword = self.m_Scanner.Keyword()
802
+ if t_strKeyword in ["d", "ds", "t", "ts"]:
803
+ # Recognized dts token
804
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(TOKEN.UNKNOWN, "{"))
805
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(TOKEN.DTS, t_strKeyword))
806
+ else:
807
+ # wasn't a dts keyword, restore to check point
808
+ self.m_Scanner.RestoreCheckPoint(t_CP)
809
+ self.m_Tokens.Append(intersystems_iris.dbapi.preparser._Token._Token(TOKEN.UNKNOWN, "{"))
810
+
811
+ # Resolve parameters and perform appropriate substitutions
812
+ def Resolve(self, p_strInput, p_Parameters):
813
+ pOut = PreParseResult()
814
+ pOut.p_eStmtType = StatementType.UPDATE
815
+ if self.ParamInfoGet() == None:
816
+ self.ParamInfoSet(intersystems_iris._IRISList._IRISList())
817
+ else:
818
+ self.ParamInfoGet().clear() # reset buffer
819
+ # Get an enumerator on the token collection
820
+ t_Enum = self.m_Tokens.GetEnumerator()
821
+ for i in range(1):
822
+ # If Parameter list is not empty prior then we have bound parameters
823
+ # from a previous parse (or user inputted?)
824
+ t_bBoundParameters = (len(p_Parameters._params_list) > 0)
825
+ if self.m_Tokens.Count() < 2:
826
+ pOut.sResult = p_strInput
827
+ break # Resolved
828
+ # Make first token current (we know we have at least 2 tokens)
829
+ t_Enum.MoveNext()
830
+ t_str = t_Enum.Current().UpperLexeme
831
+ # TODO: comments are not skipped when the enumerator is reset later in the algorithm; does this need to be fixed? Is this worth fixing?
832
+ while TOKEN.UNKNOWN == t_Enum.Current().TokenTypeGet() and t_str.startswith("/*"):
833
+ t_Enum.MoveNext() # skip comments
834
+ t_str = t_Enum.Current().UpperLexeme
835
+ # Determine statement types that need further processing
836
+ if t_str in self.s_ParsedStatements:
837
+ pOut.p_eStmtType = self.s_ParsedStatements[t_str]
838
+ self.CacheOnServerSet(True)
839
+ else:
840
+ if t_str in self.s_StatementTable:
841
+ pOut.p_eStmtType = self.s_StatementTable[t_str]
842
+ # Copy the whole statement to the output
843
+ if self.m_bBracketSubstitution and self.m_bDelimitedIdentifiers:
844
+ t_Enum.Reset()
845
+ while t_Enum.MoveNext():
846
+ pOut.sResult += t_Enum.Current().Lexeme + " "
847
+ else:
848
+ # Copy the whole statement to the output and ignore tokenizing
849
+ # syntax can fail if not exact
850
+ pOut.sResult += p_strInput
851
+ if t_str == "EXPLAIN" and pOut.p_eStmtType == StatementType.CALLWITHRESULT:
852
+ pQuery = p_strInput
853
+ pAlt = "ShowPlan"
854
+ pStat = "0"
855
+ pQuery = pQuery[(pQuery.upper().find("EXPLAIN") + len("EXPLAIN")):] # slice off "EXPLAIN"
856
+ while t_Enum.MoveNext():
857
+ if t_Enum.Current().UpperLexeme == "ALT":
858
+ pAlt = "ShowPlanAlt"
859
+ pQuery = pQuery[(pQuery.upper().find("ALT") + len("ALT")):] # slice off "ALT"
860
+ elif t_Enum.Current().UpperLexeme == "STAT":
861
+ pStat = "1"
862
+ pQuery = pQuery[(pQuery.upper().find("STAT") + len("STAT")):] # slice off "STAT"
863
+ else:
864
+ p_Parameters._clear()
865
+ p_Parameters._params_list.append(intersystems_iris.dbapi._Parameter._Parameter(pQuery, ParameterMode.REPLACED_LITERAL))
866
+ p_Parameters._params_list.append(intersystems_iris.dbapi._Parameter._Parameter(pStat, ParameterMode.REPLACED_LITERAL))
867
+ p_Parameters._params_list.append(intersystems_iris.dbapi._Parameter._Parameter(pAlt, ParameterMode.REPLACED_LITERAL))
868
+ p_Parameters._params_list.append(intersystems_iris.dbapi._Parameter._Parameter("", ParameterMode.REPLACED_LITERAL))
869
+ pOut.sResult = "select %SYSTEM . QUERY_PLAN ( :%qpar(1) , :%qpar(2) , :%qpar(3) , :%qpar(4) ) as Plan"
870
+ pOut.p_eStmtType = StatementType.QUERY
871
+ self.m_ParamInfo.add(4)
872
+ self.m_ParamInfo.add('c')
873
+ self.m_ParamInfo.add(2)
874
+ self.m_ParamInfo.add('c')
875
+ self.m_ParamInfo.add(1)
876
+ self.m_ParamInfo.add('c')
877
+ self.m_ParamInfo.add(1)
878
+ self.m_ParamInfo.add('c')
879
+ self.m_ParamInfo.add(1)
880
+ self.CacheOnServerSet(False)
881
+ return pOut
882
+ break # Resolved
883
+ else:
884
+ if t_str in self.s_TransactionStatements:
885
+ self.CacheOnServerSet(True)
886
+ else:
887
+ if t_str.startswith("("):
888
+ if t_Enum.MoveNext():
889
+ t_str = t_Enum.Current().UpperLexeme
890
+ if t_str == "SELECT":
891
+ pOut.p_eStmtType = self.s_ParsedStatements[t_str]
892
+ t_Enum.MovePrevious()
893
+ self.CacheOnServerSet(True)
894
+ if self.m_Tokens.First().GetValue().UpperEquals("SET"):
895
+ # Resolve "SET TRANSACTION" and "SET OPTION"
896
+ t_NewEnum = self.m_Tokens.GetEnumerator()
897
+ t_NewEnum.MoveNext() # "SET" is current
898
+ bMoveNext = t_NewEnum.MoveNext() # token after "SET" is current (if any)
899
+ if bMoveNext and t_NewEnum.Current().UpperEquals("TRANSACTION"):
900
+ self.CacheOnServerSet(True)
901
+ if 5 == self.m_Tokens.Count():
902
+ if bMoveNext and t_NewEnum.Current().UpperEquals("OPTION"):
903
+ bMoveNext = t_NewEnum.MoveNext()
904
+ if bMoveNext and t_NewEnum.Current().UpperEquals("BLOB_SUPPORT"):
905
+ bMoveNext = t_NewEnum.MoveNext()
906
+ if bMoveNext and t_NewEnum.Current().UpperEquals("="):
907
+ bMoveNext = t_NewEnum.MoveNext()
908
+ if bMoveNext and t_NewEnum.Current().UpperEquals("1"):
909
+ pOut.p_eStmtType = StatementType.STREAMS_ON
910
+ elif bMoveNext and t_NewEnum.Current().UpperEquals("0"):
911
+ pOut.p_eStmtType = StatementType.STREAMS_OFF
912
+ else:
913
+ raise Exception("BLOB_SUPPORT must be 0 or 1")
914
+ else:
915
+ raise Exception("Expected '=' after BLOB_SUPPORT")
916
+ elif bMoveNext and t_NewEnum.Current().UpperEquals("SYNCHRONOUS_COMMIT"):
917
+ bMoveNext = t_NewEnum.MoveNext()
918
+ if bMoveNext and t_NewEnum.Current().UpperEquals("="):
919
+ bMoveNext = t_NewEnum.MoveNext()
920
+ if bMoveNext and t_NewEnum.Current().UpperEquals("1"):
921
+ pOut.p_eStmtType = StatementType.SYNC_COMMIT
922
+ elif bMoveNext and t_NewEnum.Current().UpperEquals("0"):
923
+ pOut.p_eStmtType = StatementType.ASYNC_COMMIT
924
+ else:
925
+ raise Exception("SYNCHRONOUS_COMMIT must be 0 or 1")
926
+ else:
927
+ raise Exception("Expected '=' after SYNCHRONOUS_COMMIT")
928
+ else:
929
+ # aren't there other options beyond BLOB_SUPPORT and SYNCHRONOUS_COMMIT?
930
+ raise Exception("Unknown SET OPTION")
931
+ t_Enum.Reset()
932
+ while t_Enum.MoveNext():
933
+ pOut.sResult += t_Enum.Current().Lexeme + " "
934
+ break # Resolved
935
+ # check for Exec and Call statements
936
+ if (not self.CacheOnServerGet()) and self.Exec(pOut, p_Parameters):
937
+ self.CacheOnServerSet(True)
938
+ break
939
+ self.m_nUndefinedCount = 0
940
+ if (not self.CacheOnServerGet()) and self.Call(pOut, p_Parameters):
941
+ self.CacheOnServerSet(True)
942
+ break
943
+
944
+ pOut.sResult = ""
945
+ t_Enum.Reset()
946
+
947
+ self.t_nOpenParen = 0 # keeps track of number of open parentheses
948
+ self.t_nOrdinal = 0 # keeps track of where in p_Parameters new parameters will be inserted
949
+ self.t_nRound = 0 # keeps track of which argument of ROUND is being parsed
950
+ self.t_nRoundNested = 0 # keeps track of any nested parentheses inside of a ROUND argument
951
+
952
+ self.orderbyToken = None
953
+ self.lastToken = None # previous token that was resolved (not counting things like parentheses and commas)
954
+
955
+ t_bQuitLoop = False # currently nothing meaningful is done with this
956
+ bFirstElement = True
957
+ resolve_switcher = {
958
+ TOKEN.QUESTION_MARK: self.Resolve_question_mark,
959
+ TOKEN.ATSIGN: self.Resolve_atsign,
960
+ TOKEN.HEX: self.Resolve_hex,
961
+ TOKEN.ID: functools.partial(self.Resolve_id, stmtType = pOut.p_eStmtType),
962
+ TOKEN.STRFUNCTION: self.Resolve_strfunction,
963
+ TOKEN.DATATYPE: self.Resolve_datatype,
964
+ TOKEN.OPEN_PAREN: self.Resolve_open_paren,
965
+ TOKEN.CLOSE_PAREN: self.Resolve_close_paren,
966
+ TOKEN.OP: self.Resolve_op,
967
+ TOKEN.CONSTANT: self.Resolve_constant,
968
+ # TOKEN.NULL: self.Resolve_null,
969
+ TOKEN.COMMA: self.Resolve_comma
970
+ }
971
+ while (not t_bQuitLoop) and t_Enum.MoveNext():
972
+ t_Token = t_Enum.Current()
973
+ if bFirstElement:
974
+ bFirstElement = False
975
+ if t_Token.UpperEquals("{"):
976
+ raise Exception("'{' encountered at the beginning of the statement") # , "37000", 37000)
977
+
978
+ resolve_func = resolve_switcher.get(t_Token.TokenTypeGet(), None)
979
+ if resolve_func is not None:
980
+ t_bQuitLoop = resolve_func(p_Parameters, t_Enum, t_Token, t_bBoundParameters)
981
+
982
+ if t_Token.TokenTypeGet() not in [TOKEN.COMMA, TOKEN.OPEN_PAREN, TOKEN.CLOSE_PAREN]:
983
+ self.lastToken = t_Token
984
+
985
+ # now that we've resolved every token, need to replace parameters with ":%qpar" syntax
986
+ t_Enum.Reset()
987
+ t_nParamIndex = 1
988
+ t_count = 0
989
+
990
+ bExecute = False
991
+ while t_Enum.MoveNext():
992
+ t_count += 1
993
+ t_Token = t_Enum.Current()
994
+
995
+ # exclude an initial "EXECUTE" from the final preparsed statement
996
+ if t_Token.UpperEquals("EXECUTE"):
997
+ bExecute = True
998
+ if (2 == t_count) and (bExecute):
999
+ if t_Token.UpperEquals("SELECT"):
1000
+ pOut.p_eStmtType = StatementType.QUERY
1001
+ pOut.sResult = ""
1002
+ elif t_Token.UpperEquals("UPDATE") or t_Token.UpperEquals("INSERT"):
1003
+ pOut.p_eStmtType = StatementType.UPDATE
1004
+ pOut.sResult = ""
1005
+
1006
+ if TOKEN.QUESTION_MARK == t_Token.TokenTypeGet() or TOKEN.ATSIGN == t_Token.TokenTypeGet():
1007
+ pOut.sResult += "?" if self.embedded else ":%qpar({0})".format(t_nParamIndex)
1008
+ t_nParamIndex += 1
1009
+ if t_count < t_Enum.Count():
1010
+ pOut.sResult += ' '
1011
+ else:
1012
+ pOut.sResult += t_Token.Lexeme
1013
+ if t_count < t_Enum.Count():
1014
+ pOut.sResult += ' '
1015
+ if t_Token.UpperEquals("SELECT"):
1016
+ pOut.sResult = self.appendRowId(pOut.sResult)
1017
+ if t_Token.UpperEquals("ORDER"):
1018
+ haveMore = t_Enum.MoveNext()
1019
+ if haveMore:
1020
+ pOut.sResult += t_Enum.Current().Lexeme
1021
+ if t_count < t_Enum.Count():
1022
+ pOut.sResult += ' '
1023
+ if t_Enum.Current().UpperEquals("BY"):
1024
+ pOut.sResult = self.appendIdAdded(pOut.sResult)
1025
+ # create paramInfo $list to be passed to server
1026
+ length = 0
1027
+ if len(p_Parameters._params_list) > 0:
1028
+ item = p_Parameters._params_list[0]
1029
+ if isinstance(item, list) or isinstance(item, tuple):
1030
+ length = len(item)
1031
+ else:
1032
+ length = len(p_Parameters._params_list)
1033
+ self.m_ParamInfo.add(length - self.m_ExecParamCount) #len(p_Parameters._params_list)
1034
+ if length - self.m_ExecParamCount > 0:
1035
+ t_Enum.Reset()
1036
+ nParamIndex = 1
1037
+ p_Parameters._user_index = [-1]
1038
+ while t_Enum.MoveNext():
1039
+ if TOKEN.QUESTION_MARK == t_Enum.Current().TokenTypeGet() or TOKEN.ATSIGN == t_Enum.Current().TokenTypeGet():
1040
+ if t_Enum.Current().m_replaced:
1041
+ self.m_ParamInfo.add('c')
1042
+ else:
1043
+ self.m_ParamInfo.add('?')
1044
+ p_Parameters._add_user_param(None)
1045
+ p_Parameters._user_index.append(nParamIndex - 1)
1046
+ self.m_ParamInfo.add(t_Enum.Current().m_format)
1047
+ nParamIndex += 1
1048
+ if nParamIndex == length + 1:
1049
+ break
1050
+ return pOut
1051
+
1052
+ # '?' represents a parameter; adds a parameter to p_Parameters if none were provided
1053
+ def Resolve_question_mark(self, p_Parameters, t_Enum, t_Token, t_bBoundParameters):
1054
+ self.t_nOrdinal += 1
1055
+ if not t_bBoundParameters:
1056
+ p_Parameters._params_list.append(intersystems_iris.dbapi._Parameter._Parameter("?", ParameterMode.INPUT, '?'))
1057
+ return False
1058
+
1059
+ # "@" used for named parameters
1060
+ def Resolve_atsign(self, p_Parameters, t_Enum, t_Token, t_bBoundParameters):
1061
+ self.hasNamedParameters = True
1062
+ self.t_nOrdinal += 1
1063
+ if (not t_bBoundParameters) or len(p_Parameters._params_list) == 0:
1064
+ p_Parameters._params_list.append(intersystems_iris.dbapi._Parameter._Parameter(t_Token.Lexeme, ParameterMode.UNKNOWN))
1065
+ else:
1066
+ if not matchUpParam(p_Parameters, t_Token.Lexeme, len(p_Parameters._params_list)):
1067
+ p_Parameters._params_list.append(intersystems_iris.dbapi._Parameter._Parameter(t_Token.Lexeme, ParameterMode.UNKNOWN))
1068
+ return False
1069
+
1070
+ # replaces a hex literal with a parameter
1071
+ def Resolve_hex(self, p_Parameters, t_Enum, t_Token, t_bBoundParameters):
1072
+ self.t_nOrdinal += 1
1073
+ cp = intersystems_iris.dbapi._Parameter._Parameter(bytes.fromhex(t_Token.Lexeme[2:]), ParameterMode.REPLACED_LITERAL, '?', type = intersystems_iris.dbapi._DBAPI.SQLType.BINARY)
1074
+ p_Parameters._params_list.append(cp)
1075
+ t_Token.TokenTypeSet(TOKEN.QUESTION_MARK)
1076
+ return False
1077
+
1078
+ def Resolve_id(self, p_Parameters, t_Enum, t_Token, t_bBoundParameters, stmtType):
1079
+ if self.orderbyToken is not None and t_Enum.Current().UpperEquals("UNION"):
1080
+ self.orderbyToken = None
1081
+ if self.lastToken is not None and self.lastToken == self.orderbyToken:
1082
+ self.orderbyToken = t_Token
1083
+ self.lastToken = t_Token
1084
+ return False
1085
+ # ORDER follows parameters, quit early
1086
+ if t_Token.UpperEquals("ORDER"):
1087
+ t_NewEnum = t_Enum.Clone()
1088
+ if t_NewEnum.MoveNext():
1089
+ t_NewToken = t_NewEnum.Current()
1090
+ if t_NewToken.UpperEquals("BY"):
1091
+ self.orderbyToken = t_NewToken
1092
+ if self.t_nOpenParen == 0:
1093
+ return False
1094
+ else:
1095
+ while t_Enum.MoveNext():
1096
+ if t_Enum.Current().TokenTypeGet() == TOKEN.CLOSE_PAREN:
1097
+ self.t_nOpenParen -= 1
1098
+ break
1099
+ elif (TOKEN.ID == t_Enum.Current().TokenTypeGet()) and (t_Enum.Current().UpperEquals("UNION")):
1100
+ break
1101
+ # JSON_TABLE should have no literal substitution
1102
+ if t_Token.UpperContains("JSON_") or t_Token.UpperContains("_JSON"):
1103
+ startParen = self.t_nOpenParen
1104
+ while t_Enum.MoveNext():
1105
+ if t_Enum.Current().TokenTypeGet() == TOKEN.OPEN_PAREN:
1106
+ self.t_nOpenParen += 1
1107
+ if t_Enum.Current().TokenTypeGet() == TOKEN.CLOSE_PAREN:
1108
+ self.t_nOpenParen -= 1
1109
+ if self.t_nOpenParen == startParen:
1110
+ break
1111
+ # ROUND special handling for second parameter
1112
+ if t_Token.UpperEquals("ROUND"):
1113
+ if stmtType == StatementType.QUERY and self.t_nRound == 0:
1114
+ self.t_nRound = 1
1115
+ # DATEPART with first parameter sent as is, not a literal
1116
+ if t_Token.UpperEquals("DATEPART") or t_Token.UpperEquals("TIMESTAMPADD") or t_Token.UpperEquals("TIMESTAMPDIFF"):
1117
+ if t_Enum.MoveNext():
1118
+ if t_Enum.Current().TokenTypeGet() == TOKEN.OPEN_PAREN:
1119
+ while t_Enum.MoveNext():
1120
+ if t_Enum.Current().TokenTypeGet() == TOKEN.CONSTANT:
1121
+ t_Enum.Current().TokenTypeSet(TOKEN.ID)
1122
+ break
1123
+ if t_Enum.Current().TokenTypeGet() in [TOKEN.COMMA, TOKEN.CLOSE_PAREN]:
1124
+ break
1125
+ else:
1126
+ t_Enum.MovePrevious()
1127
+ return False
1128
+
1129
+ # I honestly have no idea why this method does what it does
1130
+ def Resolve_strfunction(self, p_Parameters, t_Enum, t_Token, t_bBoundParameters):
1131
+ parenLevel = 0
1132
+ t_TokenLast = None
1133
+ inOrderBy = False
1134
+ while t_Enum.MoveNext():
1135
+ if t_TokenLast is not None and t_TokenLast.UpperLexeme == "ORDER":
1136
+ if t_Enum.Current().UpperLexeme == "BY":
1137
+ inOrderBy = True
1138
+ t_TokenLast = t_Enum.Current()
1139
+ if parenLevel == 1 and t_Enum.Current().TokenTypeGet() == TOKEN.COMMA:
1140
+ while t_Enum.MoveNext():
1141
+ if t_Enum.Current().TokenTypeGet() == TOKEN.CONSTANT:
1142
+ t_Enum.Current().TokenTypeSet(TOKEN.ID)
1143
+ if parenLevel == 1:
1144
+ break
1145
+ elif t_Enum.Current().TokenTypeGet() == TOKEN.OPEN_PAREN:
1146
+ parenLevel += 1
1147
+ elif t_Enum.Current().TokenTypeGet() == TOKEN.CLOSE_PAREN:
1148
+ parenLevel -= 1
1149
+ if parenLevel == 1:
1150
+ break
1151
+ elif t_Enum.Current().TokenTypeGet() == TOKEN.OPEN_PAREN:
1152
+ parenLevel += 1
1153
+ elif t_Enum.Current().TokenTypeGet() == TOKEN.CLOSE_PAREN:
1154
+ if parenLevel == 1:
1155
+ break
1156
+ parenLevel -= 1
1157
+ elif t_Enum.Current().TokenTypeGet() == TOKEN.CONSTANT:
1158
+ bSubstitute = not inOrderBy
1159
+ if parenLevel > 1:
1160
+ t_Enum.MovePrevious()
1161
+ if TOKEN.OPEN_PAREN == t_Enum.Current().TokenTypeGet():
1162
+ t_Enum.MoveNext()
1163
+ t_Enum.MoveNext()
1164
+ if TOKEN.CLOSE_PAREN == t_Enum.Current().TokenTypeGet():
1165
+ bSubstitute = False
1166
+ t_Enum.MovePrevious()
1167
+ else:
1168
+ t_Enum.MoveNext()
1169
+ if bSubstitute:
1170
+ t_Token = t_Enum.Current()
1171
+ self.t_nOrdinal = self.DynamicVariable(t_bBoundParameters, t_Token, self.t_nOrdinal, p_Parameters)
1172
+ if parenLevel == 0:
1173
+ break
1174
+ return False
1175
+
1176
+ # Skips over the data type's arguments (if any)
1177
+ def Resolve_datatype(self, p_Parameters, t_Enum, t_Token, t_bBoundParameters):
1178
+ t_NewEnum = t_Enum.Clone()
1179
+ if t_NewEnum.MoveNext():
1180
+ t_NewToken = t_NewEnum.Current()
1181
+ if TOKEN.OPEN_PAREN == t_NewToken.TokenTypeGet():
1182
+ while t_NewEnum.MoveNext():
1183
+ t_NewToken = t_NewEnum.Current()
1184
+ if t_NewToken.TokenTypeGet() == TOKEN.CLOSE_PAREN:
1185
+ break
1186
+ t_Enum = t_NewEnum
1187
+ return False
1188
+
1189
+ # generally just increments t_nOpenParen (and t_nRoundNested, when relevant),
1190
+ # but also checks for "((CONSTANT))" syntax (this is a way you can get the preparser to not replace a constant with a parameter)
1191
+ def Resolve_open_paren(self, p_Parameters, t_Enum, t_Token, t_bBoundParameters):
1192
+ self.t_nOpenParen += 1
1193
+ t_NewEnum = t_Enum.Clone()
1194
+ if self.t_nRound > 0:
1195
+ self.t_nRoundNested += 1
1196
+ if t_NewEnum.MoveNext():
1197
+ t_NewToken = t_NewEnum.Current()
1198
+ if TOKEN.OPEN_PAREN == t_NewToken.TokenTypeGet():
1199
+ self.t_nOpenParen += 1
1200
+ if t_NewEnum.MoveNext():
1201
+ t_NewToken = t_NewEnum.Current()
1202
+ bCurlyBrace = (t_NewToken.Lexeme == "{")
1203
+ if TOKEN.CONSTANT == t_NewToken.TokenTypeGet() or bCurlyBrace:
1204
+ if t_NewEnum.MoveNext():
1205
+ t_NewToken = t_NewEnum.Current()
1206
+ if bCurlyBrace:
1207
+ while t_NewToken.Lexeme != "}":
1208
+ if not t_NewEnum.MoveNext():
1209
+ bCurlyBrace = False
1210
+ break
1211
+ t_NewToken = t_NewEnum.Current()
1212
+ bCurlyBrace = False
1213
+ if not t_NewEnum.MoveNext():
1214
+ return False
1215
+ t_NewToken = t_NewEnum.Current()
1216
+ if TOKEN.CLOSE_PAREN == t_NewToken.TokenTypeGet():
1217
+ self.t_nOpenParen -= 1
1218
+ if t_NewEnum.MoveNext():
1219
+ t_NewToken = t_NewEnum.Current()
1220
+ if TOKEN.CLOSE_PAREN == t_NewToken.TokenTypeGet():
1221
+ self.t_nOpenParen -= 1
1222
+ t_Enum = t_NewEnum
1223
+ if self.t_nRound > 0:
1224
+ self.t_nRoundNested -= 1
1225
+ return False
1226
+
1227
+ # decrements t_nOpenParen (and t_nRoundNested, when relevant)
1228
+ def Resolve_close_paren(self, p_Parameters, t_Enum, t_Token, t_bBoundParameters):
1229
+ if self.t_nRound > 0:
1230
+ self.t_nRoundNested -= 1
1231
+ self.t_nOpenParen -= 1
1232
+ return False
1233
+
1234
+ # skips over "(CONSTANT)" after an operator (another way to get the preparser to not replace a constant with a parameter)
1235
+ def Resolve_op(self, p_Parameters, t_Enum, t_Token, t_bBoundParameters):
1236
+ t_NewEnum = t_Enum.Clone()
1237
+ if t_NewEnum.MoveNext():
1238
+ t_NewToken = t_NewEnum.Current()
1239
+ if TOKEN.OPEN_PAREN == t_NewToken.TokenTypeGet():
1240
+ if t_NewEnum.MoveNext() and t_NewEnum.Current().TokenTypeGet() == TOKEN.CONSTANT:
1241
+ if t_NewEnum.MoveNext() and t_NewEnum.Current().TokenTypeGet() == TOKEN.CLOSE_PAREN:
1242
+ t_Enum = t_NewEnum
1243
+ return False
1244
+
1245
+ def Resolve_constant(self, p_Parameters, t_Enum, t_Token, t_bBoundParameters):
1246
+ # the second argument (and beyond?) of ROUND should not be replaced with a parameter
1247
+ if (self.t_nRound == 2) and (self.t_nRoundNested == 1):
1248
+ t_Enum.MoveNext()
1249
+ if TOKEN.COMMA != t_Enum.Current().TokenTypeGet():
1250
+ self.t_nRound = 0
1251
+ self.t_nRoundNested = 0
1252
+ t_Enum.MovePrevious()
1253
+ return False
1254
+ # Detect and Skip IN clause
1255
+ if self.lastToken is not None:
1256
+ if self.lastToken == self.orderbyToken:
1257
+ self.orderbyToken = t_Token
1258
+ self.lastToken = t_Token
1259
+ return False
1260
+ t_NewEnum = t_Enum.Clone()
1261
+
1262
+ # not 100% sure what this block does
1263
+ if t_NewEnum.MoveNext() and t_NewEnum.Current().TokenTypeGet() == TOKEN.CLOSE_PAREN:
1264
+ t_NewEnum.MovePrevious()
1265
+ if t_NewEnum.MovePrevious() and (t_NewEnum.Current().Lexeme[0] == '-'):
1266
+ t_NewEnum.MovePrevious()
1267
+ if t_Enum.Current() is not None and t_NewEnum.Current().TokenTypeGet() == TOKEN.OPEN_PAREN:
1268
+ t_NewEnum.MovePrevious()
1269
+ if t_Enum.Current() is not None:
1270
+ if TOKEN.ID != t_NewEnum.Current().TokenTypeGet() or (t_NewEnum.Current().UpperLexeme in _PreParser.s_replaceparm):
1271
+ t_Enum.MoveNext()
1272
+ return False
1273
+
1274
+ # determine format the constant will be sent to the server in (stored in paramInfo at the end of Resolve())
1275
+ if t_Enum.Current() is not None:
1276
+ c = t_Enum.Current().Lexeme
1277
+ if c[0] == '\'' or c[0] == '"':
1278
+ if c[-1] != c[0]:
1279
+ raise Exception("unmatched quote in " + t_Enum.Current().Lexeme)
1280
+ t_Enum.Current().m_format = intersystems_iris.dbapi.preparser._Token._Token.CAST_CHAR
1281
+ else:
1282
+ isInt = True
1283
+ for ii in range(len(c)):
1284
+ if c[ii] in ['.', 'e', 'E']:
1285
+ isInt = False
1286
+ break
1287
+ if isInt:
1288
+ if (21 < len(c)) or ((c[0] == '-') and (20 < len(c))):
1289
+ t_Enum.Current().m_format = intersystems_iris.dbapi.preparser._Token._Token.CAST_CHAR
1290
+ else:
1291
+ t_Enum.Current().m_format = intersystems_iris.dbapi.preparser._Token._Token.CAST_INT
1292
+ else:
1293
+ t_Enum.Current().m_format = intersystems_iris.dbapi.preparser._Token._Token.CAST_NUM
1294
+ self.t_nOrdinal = self.DynamicVariable(t_bBoundParameters, t_Token, self.t_nOrdinal, p_Parameters)
1295
+ return False
1296
+
1297
+ # not sure why this does what it does
1298
+ def Resolve_null(self, p_Parameters, t_Enum, t_Token, t_bBoundParameters):
1299
+ t_NewEnum = t_Enum.Clone()
1300
+ if t_NewEnum.MovePrevious():
1301
+ t_PreviousToken = t_NewEnum.Current()
1302
+ if t_PreviousToken.TokenTypeGet() not in [TOKEN.NOT, TOKEN.IS, TOKEN.THEN, TOKEN.COMMA, TOKEN.OPEN_PAREN, TOKEN.ELSE]:
1303
+ self.t_nOrdinal = Null(t_bBoundParameters, t_Token, self.t_nOrdinal, p_Parameters)
1304
+ return False
1305
+
1306
+ def Resolve_comma(self, p_Parameters, t_Enum, t_Token, t_bBoundParameters):
1307
+ if (self.t_nRoundNested == 1) and (self.t_nRound == 1):
1308
+ self.t_nRound += 1
1309
+ return False
1310
+
1311
+ # no idea what this does, I don't think it's used anywhere, but I kept it in just in case
1312
+ @classmethod
1313
+ def GetHexVal(cls, hex):
1314
+ """ generated source for method GetHexVal """
1315
+ val = int(hex)
1316
+ return val - (48 if val < 58 else (55 if val < 97 else 87))
1317
+
1318
+ # not sure why this does what it does
1319
+ def Null(self, p_bBoundParameters, p_Token, p_nOrdinal, p_Parameters):
1320
+ p_nOrdinal += 1
1321
+ t_Parameter = intersystems_iris.dbapi._Parameter._Parameter(mode = ParameterMode.DEFAULT_PARAMETER, paramType = 'c')
1322
+ if p_bBoundParameters:
1323
+ p_Parameters._params_list.insert(p_nOrdinal - 1, t_Parameter)
1324
+ else:
1325
+ p_Parameters._params_list.append(t_Parameter)
1326
+ p_Token.Lexeme = "?"
1327
+ p_Token.TokenTypeSet(TOKEN.QUESTION_MARK)
1328
+ p_Token.m_replaced = True
1329
+ return p_nOrdinal
1330
+
1331
+ # matches a named parameter in the SQL statement with a param in the list of parameters
1332
+ # param - list of parameters (p_Parameters from Resolve())
1333
+ # paramName - name of the parameter to be matched
1334
+ # numParam - len(param)
1335
+ def matchUpParam(self, param, paramName, numParam):
1336
+ match = False
1337
+ if not self.hasNamedParameters or (paramName == None or paramName == "" or paramName[0] != '@'):
1338
+ return False
1339
+ for i in range(len(param._params_list)):
1340
+ if (param._params_list[i].name.upper() == paramName.upper()) or (("@" + param._params_list[i].name.upper()) == paramName.upper()):
1341
+ match = True
1342
+ if i != numParam:
1343
+ cp = param._params_list[i]
1344
+ cporig = cp
1345
+ if not cporig.parsermatched:
1346
+ del param._params_list[i:(i+1)]
1347
+ else:
1348
+ cp = cporig.Clone()
1349
+ cp.name = cporig.name + str(numParam)
1350
+ cp.mode = ParameterMode.UNKNOWN
1351
+ if cporig.matchedParameterList == None:
1352
+ cporig.matchedParameterList = []
1353
+ cporig.matchedParamaterList.append(cp)
1354
+ cp.parsermatched = True
1355
+ param._params_list.insert(numParam, cp)
1356
+ else:
1357
+ param._params_list[i].parsermatched = True
1358
+ break
1359
+ return match
1360
+
1361
+ # I don't 100% follow this function, but I'm pretty sure it spends most of its time trying to isolate a return parameter, if any, then preparses as normal (?)
1362
+ def Call(self, pOut, p_Parameters):
1363
+ t_bRet = False
1364
+ pOut.p_eStmtType = StatementType.UPDATE
1365
+ pOut.sResult = ""
1366
+ for i in range(1):
1367
+ t_Enum = self.m_Tokens.GetEnumerator()
1368
+ t_Enum.MoveNext()
1369
+ t_str = t_Enum.Current().UpperLexeme
1370
+ while (TOKEN.UNKNOWN == t_Enum.Current().TokenTypeGet()) and t_str.startswith("/*"):
1371
+ t_Enum.MoveNext() # skip comments
1372
+ t_str = t_Enum.Current().UpperLexeme
1373
+ t_Token = t_Enum.Current()
1374
+ if t_Token.Lexeme[0] == '{':
1375
+ t_Enum.MoveNext()
1376
+ t_Token = t_Enum.Current()
1377
+ returnParam = None
1378
+ # expects either "? = ..." or one of "CALL", "EXEC", "EXECUTE"
1379
+ if t_Token.TokenTypeGet() == TOKEN.QUESTION_MARK:
1380
+ returnParam = intersystems_iris.dbapi._Parameter._Parameter("?", ParameterMode.RETURN_VALUE, '?')
1381
+ if not t_Enum.MoveNext() or t_Enum.Current().Lexeme[0] != '=':
1382
+ break
1383
+ if not t_Enum.MoveNext():
1384
+ break
1385
+ elif not (t_Enum.Current().UpperEquals("CALL") or t_Enum.Current().UpperEquals("EXEC") or t_Enum.Current().UpperEquals("EXECUTE")):
1386
+ return False
1387
+
1388
+ # not really sure what to make of the next couple blocks of code
1389
+ # feels like they should maybe be in another elif block, not their own if block
1390
+ if t_Token.TokenTypeGet() == TOKEN.ATSIGN:
1391
+ self.hasNamedParameters = True
1392
+ returnParam = intersystems_iris.dbapi._Parameter._Parameter(t_Token.Lexeme, ParameterMode.RETURN_VALUE)
1393
+ if not t_Enum.MoveNext() or t_Enum.Current().Lexeme[0] != '=':
1394
+ break
1395
+ if not t_Enum.MoveNext():
1396
+ break
1397
+ if t_Enum.Current().UpperEquals("CALL") or t_Enum.Current().UpperEquals("EXEC") or t_Enum.Current().UpperEquals("EXECUTE"):
1398
+ if not t_Enum.MoveNext():
1399
+ break
1400
+ else:
1401
+ if TOKEN.STRFUNCTION == t_Enum.Current().TokenTypeGet():
1402
+ break
1403
+
1404
+ pOut.sResult += t_Enum.Current().Lexeme
1405
+ t_Token = t_Enum.Current()
1406
+ if t_Token.UpperEquals("SELECT") or t_Token.UpperEquals("UPDATE") or t_Token.UpperEquals("INSERT"):
1407
+ pOut.sResult = ""
1408
+ break
1409
+ if not t_Enum.MoveNext():
1410
+ break
1411
+ t_Token = t_Enum.Current()
1412
+ if t_Token.UpperEquals("SELECT") or t_Token.UpperEquals("UPDATE") or t_Token.UpperEquals("INSERT"):
1413
+ pOut.sResult = ""
1414
+ break
1415
+ t_bQuitLoop = False
1416
+ while t_Token.Lexeme[0] == '.':
1417
+ pOut.sResult += '.'
1418
+ if not t_Enum.MoveNext():
1419
+ t_bQuitLoop = True
1420
+ break
1421
+ t_Token = t_Enum.Current()
1422
+ if t_Token.TokenTypeGet() == TOKEN.ID:
1423
+ pOut.sResult += t_Token.Lexeme
1424
+ if not t_Enum.MoveNext():
1425
+ t_bQuitLoop = True
1426
+ break
1427
+ t_Token = t_Enum.Current()
1428
+ t_bBoundParameters = (len(p_Parameters._params_list) > 0)
1429
+ t_nOrdinal = 0
1430
+ if returnParam is not None:
1431
+ t_nOrdinal += 1
1432
+ if not t_bBoundParameters:
1433
+ p_Parameters._params_list.insert(0, returnParam)
1434
+ else:
1435
+ if not matchUpParam(p_Parameters, returnParam.GetName(), t_nOrdinal - 1):
1436
+ if p_Parameters._params_list[0].mode != ParameterMode.RETURN_VALUE:
1437
+ p_Parameters._params_list.insert(0, returnParam)
1438
+ if not t_bQuitLoop:
1439
+ t_eLastToken = TOKEN.UNKNOWN
1440
+ call_switcher = {
1441
+ TOKEN.QUESTION_MARK: self.Call_question_mark,
1442
+ TOKEN.ATSIGN: self.Call_atsign,
1443
+ TOKEN.HEX: self.Call_hex,
1444
+ TOKEN.CONSTANT: functools.partial(self.Call_constant_id, t_Enum = t_Enum),
1445
+ TOKEN.ID: functools.partial(self.Call_constant_id, t_Enum = t_Enum),
1446
+ TOKEN.NULL: self.Call_null,
1447
+ TOKEN.COMMA: functools.partial(self.Call_comma_paren, t_eLastToken = t_eLastToken),
1448
+ TOKEN.CLOSE_PAREN: functools.partial(self.Call_comma_paren, t_eLastToken = t_eLastToken)
1449
+ }
1450
+ while True:
1451
+ t_Token = t_Enum.Current()
1452
+ call_func = call_switcher.get(t_Token.TokenTypeGet(), self.Call_default)
1453
+ (t_nOrdinal, t_eLastToken) = call_func(p_Parameters, t_Token, t_nOrdinal, t_bBoundParameters)
1454
+
1455
+ if not t_Enum.MoveNext():
1456
+ break
1457
+ pOut.p_eStmtType = StatementType.CALL if (returnParam == None) else StatementType.CALLWITHRESULT
1458
+ t_bRet = True
1459
+ return t_bRet
1460
+
1461
+ def Call_default(self, p_Parameters, t_Token, t_nOrdinal, t_bBoundParameters):
1462
+ return (t_nOrdinal, t_Token.TokenTypeGet())
1463
+
1464
+ def Call_question_mark(self, p_Parameters, t_Token, t_nOrdinal, t_bBoundParameters):
1465
+ if not t_bBoundParameters:
1466
+ p_Parameters._params_list.append(intersystems_iris.dbapi._Parameter._Parameter("?", ParameterMode.INPUT, '?'))
1467
+ return (t_nOrdinal + 1, TOKEN.QUESTION_MARK)
1468
+
1469
+ def Call_atsign(self, p_Parameters, t_Token, t_nOrdinal, t_bBoundParameters):
1470
+ self.hasNamedParameters = True
1471
+ if (not t_bBoundParameters) or len(p_Parameters._params_list) == 0:
1472
+ p_Parameters._params_list.add(intersystems_iris.dbapi._Parameter._Parameter(t_Token.Lexeme, ParameterMode.UNKNOWN))
1473
+ else:
1474
+ if not matchUpParam(p_Parameters, t_Token.Lexeme, t_nOrdinal):
1475
+ p_Parameters._params_list.add(intersystems_iris.dbapi._Parameter._Parameter(t_Token.Lexeme, ParameterMode.UNKNOWN))
1476
+ return (t_nOrdinal + 1, TOKEN.ATSIGN)
1477
+
1478
+ def Call_hex(self, p_Parameters, t_Token, t_nOrdinal, t_bBoundParameters):
1479
+ cp = intersystems_iris.dbapi._Parameter._Parameter(bytes.fromhex(t_Token.Lexeme[2:]), ParameterMode.REPLACED_LITERAL, '?')
1480
+ p_Parameters._params_list.append(cp)
1481
+ t_Token.TokenTypeSet(TOKEN.QUESTION_MARK)
1482
+ return (t_nOrdinal + 1, TOKEN.QUESTION_MARK)
1483
+
1484
+ def Call_constant_id(self, p_Parameters, t_Token, t_nOrdinal, t_bBoundParameters, t_Enum):
1485
+ t_NewEnum = t_Enum.Clone()
1486
+ if t_NewEnum.MovePrevious():
1487
+ t_PreviousToken = t_NewEnum.Current()
1488
+ if t_PreviousToken.TokenTypeGet() == TOKEN.OP:
1489
+ t_Token.TokenTypeSet(TOKEN.QUESTION_MARK)
1490
+ return (self.DynamicVariable(t_bBoundParameters, intersystems_iris.dbapi.preparser._Token._Token(TOKEN.CONSTANT, t_PreviousToken.Lexeme, t_PreviousToken.UpperLexeme), t_nOrdinal, p_Parameters),
1491
+ TOKEN.QUESTION_MARK)
1492
+ return (self.DynamicVariable(t_bBoundParameters, t_Token, t_nOrdinal, p_Parameters), TOKEN.QUESTION_MARK)
1493
+
1494
+ def Call_null(self, p_Parameters, t_Token, t_nOrdinal, t_bBoundParameters):
1495
+ return (self.Null(t_bBoundParameters, t_Token, t_nOrdinal, p_Parameters), t_Token.TokenTypeGet())
1496
+
1497
+ def Call_comma_paren(self, p_Parameters, t_Token, t_nOrdinal, t_bBoundParameters, t_eLastToken):
1498
+ if TOKEN.COMMA == t_eLastToken or TOKEN.OPEN_PAREN == t_eLastToken:
1499
+ t_Parameter = intersystems_iris.dbapi._Parameter._Parameter(mode = ParameterMode.DEFAULT_PARAMETER, paramType = 'd')
1500
+ t_nOrdinal += 1
1501
+ self.m_nUndefinedCount += 1
1502
+ if t_bBoundParameters:
1503
+ p_Parameters._params_list.insert(t_nOrdinal - 1, t_Parameter)
1504
+ else:
1505
+ p_Parameters._params_list.append(t_Parameter)
1506
+ return (t_nOrdinal, t_Token.TokenTypeGet())
1507
+
1508
+ # No idea why this function does what it does
1509
+ def Exec(self, pOut, p_Parameters):
1510
+ t_bRet = False
1511
+ pOut.p_eStmtType = StatementType.UPDATE
1512
+ t_Enum = self.m_Tokens.GetEnumerator()
1513
+ for i in range(1):
1514
+ t_Enum.MoveNext()
1515
+ t_Token = t_Enum.Current()
1516
+ if not t_Token.UpperEquals("EXEC") and not t_Token.UpperEquals("EXECUTE"):
1517
+ break
1518
+ pOut.p_eStmtType = StatementType.CALL
1519
+ t_Enum.MoveNext()
1520
+ t_str = t_Enum.Current().UpperLexeme
1521
+ while (TOKEN.UNKNOWN == t_Enum.Current().TokenTypeGet()) and t_str.startswith("/*"):
1522
+ t_Enum.MoveNext() # skip comments
1523
+ t_str = t_Enum.Current().UpperLexeme
1524
+ t_Token = t_Enum.Current()
1525
+ if (t_Token.UpperEquals("SELECT")) or (t_Token.UpperEquals("UPDATE")) or (t_Token.UpperEquals("INSERT")):
1526
+ break
1527
+ t_bRet = True
1528
+ t_bHasReturnType = False
1529
+ if '@' == t_Token.Lexeme[0]:
1530
+ t_bHasReturnType = True
1531
+ if not t_Enum.MoveNext():
1532
+ break
1533
+ if not t_Enum.MoveNext():
1534
+ break
1535
+ t_Token = t_Enum.Current()
1536
+ if t_Token.Lexeme != "=":
1537
+ break
1538
+ if not t_Enum.MoveNext():
1539
+ break
1540
+ t_Token = t_Enum.Current()
1541
+ pOut.sResult += t_Token.Lexeme
1542
+ if not t_Enum.MoveNext():
1543
+ return True
1544
+ t_Token = t_Enum.Current()
1545
+ t_bQuitLoop = False
1546
+ while t_Token.Lexeme[0] == '.':
1547
+ pOut.sResult.append('.')
1548
+ if not t_Enum.MoveNext():
1549
+ t_bQuitLoop = True
1550
+ break
1551
+ t_Token = t_Enum.Current()
1552
+ if t_Token.TokenTypeGet() == TOKEN.ID:
1553
+ pOut.sResult.append(t_Token.Lexeme)
1554
+ if not t_Enum.MoveNext():
1555
+ t_bQuitLoop = True
1556
+ break
1557
+ t_Token = t_Enum.Current()
1558
+ if t_bQuitLoop:
1559
+ break
1560
+ t_nOrdinal = 0
1561
+ while True:
1562
+ t_Token = t_Enum.Current()
1563
+ if TOKEN.COMMA == t_Token.TokenTypeGet():
1564
+ if not t_Enum.MoveNext():
1565
+ break
1566
+ t_Token = t_Enum.Current()
1567
+ if t_Token.UpperEquals("WITH RECOMPILE"): # Shouldn't it be impossible for this to be a single token?
1568
+ break
1569
+ t_strParameterName = ""
1570
+ if t_Token.Lexeme[0] == '@':
1571
+ if t_Enum.MoveNext():
1572
+ t_strParameterName = t_Enum.Current().Lexeme
1573
+ bMoveNext = t_Enum.MoveNext()
1574
+ if (not bMoveNext) or (t_Enum.Current().Lexeme != "="):
1575
+ if not bMoveNext:
1576
+ t_bQuitLoop = True
1577
+ t_Param = intersystems_iris.dbapi._Parameter._Parameter(mode = ParameterMode.INPUT_OUTPUT, name = t_Token.Lexeme[1:], execParam = True)
1578
+ self.m_ExecParamCount += 1
1579
+ p_Parameters._params_list.append(t_Param)
1580
+ continue
1581
+ t_Enum.MoveNext()
1582
+ t_Token = t_Enum.Current()
1583
+ t_Enum.MoveNext()
1584
+ if t_Token.TokenTypeGet() not in [TOKEN.OPEN_PAREN, TOKEN.CLOSE_PAREN, TOKEN.QUESTION_MARK, TOKEN.UNKNOWN]:
1585
+ if t_Token is not None:
1586
+ if t_Token.Lexeme[0] == '-':
1587
+ if not t_Enum.MoveNext():
1588
+ t_bQuitLoop = True
1589
+ else:
1590
+ t_Token = t_Enum.Current()
1591
+ t_NewToken = intersystems_iris.dbapi.preparser._Token._Token(TOKEN.CONSTANT, "-" + t_Token.Lexem, "-" + t_Token.UpperLexeme)
1592
+ t_nOrdinal = self.DynamicVariable(False, t_NewToken, t_nOrdinal, p_Parameters)
1593
+ else:
1594
+ t_nOrdinal = self.DynamicVariable(False, t_Token, t_nOrdinal, p_Parameters)
1595
+ if not t_bQuitLoop:
1596
+ t_Parameter = p_Parameters._params_list[-1]
1597
+ t_Parameter.name = t_strParameterName
1598
+ t_Parameter.execParam = True
1599
+ self.m_ExecParamCount += 1
1600
+ if not t_Enum.MoveNext():
1601
+ break
1602
+ if t_bQuitLoop:
1603
+ break
1604
+ if t_bHasReturnType:
1605
+ pOut.p_eStmtType = StatementType.CALLWITHRESULT
1606
+ t_ReturnParam = intersystems_iris.dbapi._Parameter._Parameter(mode = ParameterMode.UNKNOWN, execParam = True)
1607
+ self.m_ExecParamCount += 1
1608
+ p_Parameters._params_list.insert(0, t_ReturnParam)
1609
+ else:
1610
+ pOut.p_eStmtType = StatementType.CALL
1611
+ t_bRet = True
1612
+ if 0 == self.m_ExecParamCount:
1613
+ return False
1614
+ return t_bRet
1615
+
1616
+ # creates Parameter object for replaced literals
1617
+ def DynamicVariable(self, p_bBoundParameters, p_Token, p_nOrdinal, p_Parameters):
1618
+ p_nOrdinal += 1
1619
+ t_str = p_Token.Lexeme
1620
+ t_c = t_str[0]
1621
+ if t_c in ["'", "\""]:
1622
+ # Remove leading and trailing quotes
1623
+ t_str = t_str[1:-1]
1624
+ # Condense doubled quotes to a single quote
1625
+ t_i = 0
1626
+ while t_i < len(t_str) - 1:
1627
+ if (t_str[t_i] == t_c) and (t_str[t_i + 1] == t_c):
1628
+ t_str = t_str[:t_i] + t_str[(t_i + 1):]
1629
+ t_i += 1
1630
+ else:
1631
+ if 'e' in t_str or 'E' in t_str:
1632
+ # Normalize number
1633
+ try:
1634
+ t_double = float(t_str)
1635
+ t_str = str(t_double)
1636
+ except ValueError:
1637
+ # wasn't able to parse, leave as is
1638
+ pass
1639
+ else:
1640
+ p = 0
1641
+ if t_str[p] == '+':
1642
+ t_str = t_str[1:]
1643
+ if t_str[p] == '-':
1644
+ p += 1
1645
+ while (p < len(t_str)) and (t_str[p] == '0'):
1646
+ t_str = t_str[:p] + t_str[(p + 1):]
1647
+ if '.' in t_str:
1648
+ while t_str[-1] == '0':
1649
+ t_str = t_str[:-1]
1650
+ if t_str[-1] == '.':
1651
+ t_str = t_str[:-1]
1652
+ if p >= len(t_str):
1653
+ t_str = "0"
1654
+ if p_bBoundParameters:
1655
+ p_Parameters._params_list.insert(p_nOrdinal - 1, intersystems_iris.dbapi._Parameter._Parameter(t_str, ParameterMode.REPLACED_LITERAL))
1656
+ else:
1657
+ p_Parameters._params_list.append(intersystems_iris.dbapi._Parameter._Parameter(t_str, ParameterMode.REPLACED_LITERAL))
1658
+ p_Token.Lexeme = "?"
1659
+ p_Token.TokenTypeSet(TOKEN.QUESTION_MARK)
1660
+ p_Token.m_replaced = True
1661
+ return p_nOrdinal
1662
+
1663
+ def appendRowId(self, sb):
1664
+ if self.m_addRowID != 0:
1665
+ return sb + "%ID ,"
1666
+ return sb
1667
+
1668
+ def appendIdAdded(self, sb):
1669
+ if self.m_addRowID == 2:
1670
+ return sb + "%IDADDED "
1671
+ return sb