@rip-lang/db 0.10.0 → 1.0.1
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.
- package/INTERNALS.md +324 -0
- package/README.md +93 -237
- package/bin/rip-db +3 -3
- package/build.zig +88 -0
- package/db.rip +66 -80
- package/lib/darwin-arm64/duckdb.node +0 -0
- package/lib/duckdb.mjs +246 -333
- package/package.json +11 -5
- package/src/duckdb.zig +1156 -0
- package/PROTOCOL.md +0 -258
- package/db.html +0 -122
- package/lib/duckdb-binary.rip +0 -525
package/lib/duckdb-binary.rip
DELETED
|
@@ -1,525 +0,0 @@
|
|
|
1
|
-
# DuckDB Binary Protocol Serializer
|
|
2
|
-
#
|
|
3
|
-
# Implements the binary serialization format used by DuckDB's official UI.
|
|
4
|
-
# This allows rip-db to serve responses that the DuckDB UI can understand.
|
|
5
|
-
#
|
|
6
|
-
# Protocol spec: See PROTOCOL.md in this directory
|
|
7
|
-
|
|
8
|
-
# ==============================================================================
|
|
9
|
-
# LogicalTypeId - matches DuckDB's internal type IDs
|
|
10
|
-
# ==============================================================================
|
|
11
|
-
|
|
12
|
-
export LogicalTypeId =
|
|
13
|
-
BOOLEAN: 10
|
|
14
|
-
TINYINT: 11
|
|
15
|
-
SMALLINT: 12
|
|
16
|
-
INTEGER: 13
|
|
17
|
-
BIGINT: 14
|
|
18
|
-
DATE: 15
|
|
19
|
-
TIME: 16
|
|
20
|
-
TIMESTAMP_SEC: 17
|
|
21
|
-
TIMESTAMP_MS: 18
|
|
22
|
-
TIMESTAMP: 19
|
|
23
|
-
TIMESTAMP_NS: 20
|
|
24
|
-
DECIMAL: 21
|
|
25
|
-
FLOAT: 22
|
|
26
|
-
DOUBLE: 23
|
|
27
|
-
CHAR: 24
|
|
28
|
-
VARCHAR: 25
|
|
29
|
-
BLOB: 26
|
|
30
|
-
INTERVAL: 27
|
|
31
|
-
UTINYINT: 28
|
|
32
|
-
USMALLINT: 29
|
|
33
|
-
UINTEGER: 30
|
|
34
|
-
UBIGINT: 31
|
|
35
|
-
TIMESTAMP_TZ: 32
|
|
36
|
-
TIME_TZ: 34
|
|
37
|
-
BIT: 36
|
|
38
|
-
BIGNUM: 39
|
|
39
|
-
UHUGEINT: 49
|
|
40
|
-
HUGEINT: 50
|
|
41
|
-
UUID: 54
|
|
42
|
-
STRUCT: 100
|
|
43
|
-
LIST: 101
|
|
44
|
-
MAP: 102
|
|
45
|
-
ENUM: 104
|
|
46
|
-
UNION: 107
|
|
47
|
-
ARRAY: 108
|
|
48
|
-
|
|
49
|
-
# ==============================================================================
|
|
50
|
-
# BinarySerializer - writes the DuckDB binary format
|
|
51
|
-
# ==============================================================================
|
|
52
|
-
|
|
53
|
-
export class BinarySerializer
|
|
54
|
-
constructor: ->
|
|
55
|
-
@buffer = []
|
|
56
|
-
|
|
57
|
-
# ---------------------------------------------------------------------------
|
|
58
|
-
# Primitive writers
|
|
59
|
-
# ---------------------------------------------------------------------------
|
|
60
|
-
|
|
61
|
-
writeUint8: (value) ->
|
|
62
|
-
@buffer.push value & 0xFF
|
|
63
|
-
|
|
64
|
-
writeUint16LE: (value) ->
|
|
65
|
-
@buffer.push value & 0xFF
|
|
66
|
-
@buffer.push (value >> 8) & 0xFF
|
|
67
|
-
|
|
68
|
-
writeUint32LE: (value) ->
|
|
69
|
-
@buffer.push value & 0xFF
|
|
70
|
-
@buffer.push (value >> 8) & 0xFF
|
|
71
|
-
@buffer.push (value >> 16) & 0xFF
|
|
72
|
-
@buffer.push (value >> 24) & 0xFF
|
|
73
|
-
|
|
74
|
-
writeInt32LE: (value) ->
|
|
75
|
-
@writeUint32LE value >>> 0
|
|
76
|
-
|
|
77
|
-
writeUint64LE: (value) ->
|
|
78
|
-
big = BigInt value
|
|
79
|
-
for i in [0...8]
|
|
80
|
-
@buffer.push Number (big >> BigInt(i * 8)) & 0xFFn
|
|
81
|
-
|
|
82
|
-
writeInt64LE: (value) ->
|
|
83
|
-
@writeUint64LE value
|
|
84
|
-
|
|
85
|
-
writeFloat32: (value) ->
|
|
86
|
-
buf = new ArrayBuffer 4
|
|
87
|
-
new DataView(buf).setFloat32 0, value, true
|
|
88
|
-
bytes = new Uint8Array buf
|
|
89
|
-
@buffer.push ...bytes
|
|
90
|
-
|
|
91
|
-
writeFloat64: (value) ->
|
|
92
|
-
buf = new ArrayBuffer 8
|
|
93
|
-
new DataView(buf).setFloat64 0, value, true
|
|
94
|
-
bytes = new Uint8Array buf
|
|
95
|
-
@buffer.push ...bytes
|
|
96
|
-
|
|
97
|
-
writeVarInt: (value) ->
|
|
98
|
-
v = value >>> 0
|
|
99
|
-
loop
|
|
100
|
-
byte = v & 0x7F
|
|
101
|
-
v >>>= 7
|
|
102
|
-
if v isnt 0
|
|
103
|
-
@buffer.push byte | 0x80
|
|
104
|
-
else
|
|
105
|
-
@buffer.push byte
|
|
106
|
-
break
|
|
107
|
-
|
|
108
|
-
writeString: (str) ->
|
|
109
|
-
encoder = new TextEncoder()
|
|
110
|
-
bytes = encoder.encode str
|
|
111
|
-
@writeVarInt bytes.length
|
|
112
|
-
@buffer.push ...bytes
|
|
113
|
-
|
|
114
|
-
writeData: (data) ->
|
|
115
|
-
bytes = if data instanceof Uint8Array then data else new Uint8Array data
|
|
116
|
-
@writeVarInt bytes.length
|
|
117
|
-
@buffer.push ...bytes
|
|
118
|
-
|
|
119
|
-
# ---------------------------------------------------------------------------
|
|
120
|
-
# Object structure writers
|
|
121
|
-
# ---------------------------------------------------------------------------
|
|
122
|
-
|
|
123
|
-
writeFieldId: (id) ->
|
|
124
|
-
@writeUint16LE id
|
|
125
|
-
|
|
126
|
-
writeEndMarker: ->
|
|
127
|
-
@writeUint16LE 0xFFFF
|
|
128
|
-
|
|
129
|
-
writeBoolean: (fieldId, value) ->
|
|
130
|
-
@writeFieldId fieldId
|
|
131
|
-
@writeUint8 if value then 1 else 0
|
|
132
|
-
|
|
133
|
-
writePropertyString: (fieldId, value) ->
|
|
134
|
-
@writeFieldId fieldId
|
|
135
|
-
@writeString value
|
|
136
|
-
|
|
137
|
-
writePropertyVarInt: (fieldId, value) ->
|
|
138
|
-
@writeFieldId fieldId
|
|
139
|
-
@writeVarInt value
|
|
140
|
-
|
|
141
|
-
writeList: (fieldId, items, writer) ->
|
|
142
|
-
@writeFieldId fieldId
|
|
143
|
-
@writeVarInt items.length
|
|
144
|
-
for item, i in items
|
|
145
|
-
writer this, item, i
|
|
146
|
-
|
|
147
|
-
# ---------------------------------------------------------------------------
|
|
148
|
-
# Get final buffer
|
|
149
|
-
# ---------------------------------------------------------------------------
|
|
150
|
-
|
|
151
|
-
toArrayBuffer: ->
|
|
152
|
-
new Uint8Array(@buffer).buffer
|
|
153
|
-
|
|
154
|
-
toUint8Array: ->
|
|
155
|
-
new Uint8Array @buffer
|
|
156
|
-
|
|
157
|
-
# ==============================================================================
|
|
158
|
-
# Result serializers
|
|
159
|
-
# ==============================================================================
|
|
160
|
-
|
|
161
|
-
export serializeSuccessResult = (columns, rows) ->
|
|
162
|
-
s = new BinarySerializer()
|
|
163
|
-
|
|
164
|
-
# field_100: success = true
|
|
165
|
-
s.writeBoolean 100, true
|
|
166
|
-
|
|
167
|
-
# field_101: ColumnNamesAndTypes
|
|
168
|
-
s.writeFieldId 101
|
|
169
|
-
serializeColumnNamesAndTypes s, columns
|
|
170
|
-
|
|
171
|
-
# field_102: list<DataChunk> (one chunk with all rows)
|
|
172
|
-
s.writeFieldId 102
|
|
173
|
-
s.writeVarInt 1
|
|
174
|
-
serializeDataChunk s, columns, rows
|
|
175
|
-
|
|
176
|
-
s.writeEndMarker()
|
|
177
|
-
s.toArrayBuffer()
|
|
178
|
-
|
|
179
|
-
export serializeErrorResult = (message) ->
|
|
180
|
-
s = new BinarySerializer()
|
|
181
|
-
s.writeBoolean 100, false
|
|
182
|
-
s.writePropertyString 101, message
|
|
183
|
-
s.writeEndMarker()
|
|
184
|
-
s.toArrayBuffer()
|
|
185
|
-
|
|
186
|
-
export serializeEmptyResult = ->
|
|
187
|
-
new BinarySerializer().toArrayBuffer()
|
|
188
|
-
|
|
189
|
-
export serializeTokenizeResult = (tokens) ->
|
|
190
|
-
s = new BinarySerializer()
|
|
191
|
-
s.writeList 100, tokens.map((t) -> t.offset), (s, v) -> s.writeVarInt v
|
|
192
|
-
s.writeList 101, tokens.map((t) -> t.type), (s, v) -> s.writeVarInt v
|
|
193
|
-
s.writeEndMarker()
|
|
194
|
-
s.toArrayBuffer()
|
|
195
|
-
|
|
196
|
-
# ==============================================================================
|
|
197
|
-
# Internal serializers
|
|
198
|
-
# ==============================================================================
|
|
199
|
-
|
|
200
|
-
serializeColumnNamesAndTypes = (s, columns) ->
|
|
201
|
-
s.writeList 100, columns, (s, col) -> s.writeString col.name
|
|
202
|
-
s.writeList 101, columns, (s, col) -> serializeType s, col
|
|
203
|
-
s.writeEndMarker()
|
|
204
|
-
|
|
205
|
-
serializeType = (s, column) ->
|
|
206
|
-
typeId = mapDuckDBType column.type
|
|
207
|
-
s.writeFieldId 100
|
|
208
|
-
s.writeUint8 typeId
|
|
209
|
-
s.writeFieldId 101
|
|
210
|
-
s.writeUint8 0 # null (no extra type info for basic types)
|
|
211
|
-
s.writeEndMarker()
|
|
212
|
-
|
|
213
|
-
serializeDataChunk = (s, columns, rows) ->
|
|
214
|
-
s.writePropertyVarInt 100, rows.length
|
|
215
|
-
s.writeList 101, columns, (s, col, colIdx) ->
|
|
216
|
-
values = rows.map (row) -> row[colIdx] ? row[col.name]
|
|
217
|
-
serializeVector s, col, values
|
|
218
|
-
s.writeEndMarker()
|
|
219
|
-
|
|
220
|
-
serializeVector = (s, column, values) ->
|
|
221
|
-
typeId = mapDuckDBType column.type
|
|
222
|
-
hasNulls = values.some (v) -> v is null or v is undefined
|
|
223
|
-
allValid = if hasNulls then 0 else 1
|
|
224
|
-
|
|
225
|
-
s.writeFieldId 100
|
|
226
|
-
s.writeUint8 allValid
|
|
227
|
-
|
|
228
|
-
if not allValid
|
|
229
|
-
s.writeFieldId 101
|
|
230
|
-
s.writeData createValidityBitmap values
|
|
231
|
-
|
|
232
|
-
switch typeId
|
|
233
|
-
when LogicalTypeId.VARCHAR, LogicalTypeId.CHAR
|
|
234
|
-
s.writeList 102, values, (s, v) -> s.writeString String(v ? '')
|
|
235
|
-
|
|
236
|
-
when LogicalTypeId.BOOLEAN
|
|
237
|
-
s.writeFieldId 102
|
|
238
|
-
bytes = new Uint8Array values.length
|
|
239
|
-
for v, i in values
|
|
240
|
-
bytes[i] = if v then 1 else 0
|
|
241
|
-
s.writeData bytes
|
|
242
|
-
|
|
243
|
-
when LogicalTypeId.TINYINT, LogicalTypeId.UTINYINT
|
|
244
|
-
s.writeFieldId 102
|
|
245
|
-
bytes = new Uint8Array values.length
|
|
246
|
-
for v, i in values
|
|
247
|
-
bytes[i] = (v ? 0) & 0xFF
|
|
248
|
-
s.writeData bytes
|
|
249
|
-
|
|
250
|
-
when LogicalTypeId.SMALLINT
|
|
251
|
-
s.writeFieldId 102
|
|
252
|
-
bytes = new Uint8Array values.length * 2
|
|
253
|
-
dv = new DataView bytes.buffer
|
|
254
|
-
for v, i in values
|
|
255
|
-
dv.setInt16 i * 2, v ? 0, true
|
|
256
|
-
s.writeData bytes
|
|
257
|
-
|
|
258
|
-
when LogicalTypeId.USMALLINT
|
|
259
|
-
s.writeFieldId 102
|
|
260
|
-
bytes = new Uint8Array values.length * 2
|
|
261
|
-
dv = new DataView bytes.buffer
|
|
262
|
-
for v, i in values
|
|
263
|
-
dv.setUint16 i * 2, v ? 0, true
|
|
264
|
-
s.writeData bytes
|
|
265
|
-
|
|
266
|
-
when LogicalTypeId.INTEGER
|
|
267
|
-
s.writeFieldId 102
|
|
268
|
-
bytes = new Uint8Array values.length * 4
|
|
269
|
-
dv = new DataView bytes.buffer
|
|
270
|
-
for v, i in values
|
|
271
|
-
dv.setInt32 i * 4, v ? 0, true
|
|
272
|
-
s.writeData bytes
|
|
273
|
-
|
|
274
|
-
when LogicalTypeId.UINTEGER
|
|
275
|
-
s.writeFieldId 102
|
|
276
|
-
bytes = new Uint8Array values.length * 4
|
|
277
|
-
dv = new DataView bytes.buffer
|
|
278
|
-
for v, i in values
|
|
279
|
-
dv.setUint32 i * 4, v ? 0, true
|
|
280
|
-
s.writeData bytes
|
|
281
|
-
|
|
282
|
-
when LogicalTypeId.BIGINT
|
|
283
|
-
s.writeFieldId 102
|
|
284
|
-
bytes = new Uint8Array values.length * 8
|
|
285
|
-
dv = new DataView bytes.buffer
|
|
286
|
-
for v, i in values
|
|
287
|
-
dv.setBigInt64 i * 8, BigInt(v ? 0), true
|
|
288
|
-
s.writeData bytes
|
|
289
|
-
|
|
290
|
-
when LogicalTypeId.UBIGINT
|
|
291
|
-
s.writeFieldId 102
|
|
292
|
-
bytes = new Uint8Array values.length * 8
|
|
293
|
-
dv = new DataView bytes.buffer
|
|
294
|
-
for v, i in values
|
|
295
|
-
dv.setBigUint64 i * 8, BigInt(v ? 0), true
|
|
296
|
-
s.writeData bytes
|
|
297
|
-
|
|
298
|
-
when LogicalTypeId.FLOAT
|
|
299
|
-
s.writeFieldId 102
|
|
300
|
-
bytes = new Uint8Array values.length * 4
|
|
301
|
-
dv = new DataView bytes.buffer
|
|
302
|
-
for v, i in values
|
|
303
|
-
dv.setFloat32 i * 4, v ? 0, true
|
|
304
|
-
s.writeData bytes
|
|
305
|
-
|
|
306
|
-
when LogicalTypeId.DOUBLE
|
|
307
|
-
s.writeFieldId 102
|
|
308
|
-
bytes = new Uint8Array values.length * 8
|
|
309
|
-
dv = new DataView bytes.buffer
|
|
310
|
-
for v, i in values
|
|
311
|
-
dv.setFloat64 i * 8, v ? 0, true
|
|
312
|
-
s.writeData bytes
|
|
313
|
-
|
|
314
|
-
when LogicalTypeId.DATE
|
|
315
|
-
s.writeFieldId 102
|
|
316
|
-
bytes = new Uint8Array values.length * 4
|
|
317
|
-
dv = new DataView bytes.buffer
|
|
318
|
-
for v, i in values
|
|
319
|
-
days = if v? then dateToDays v else 0
|
|
320
|
-
dv.setInt32 i * 4, days, true
|
|
321
|
-
s.writeData bytes
|
|
322
|
-
|
|
323
|
-
when LogicalTypeId.TIMESTAMP, LogicalTypeId.TIMESTAMP_TZ
|
|
324
|
-
s.writeFieldId 102
|
|
325
|
-
bytes = new Uint8Array values.length * 8
|
|
326
|
-
dv = new DataView bytes.buffer
|
|
327
|
-
for v, i in values
|
|
328
|
-
micros = if v? then timestampToMicros v else 0n
|
|
329
|
-
dv.setBigInt64 i * 8, micros, true
|
|
330
|
-
s.writeData bytes
|
|
331
|
-
|
|
332
|
-
else
|
|
333
|
-
s.writeList 102, values, (s, v) -> s.writeString String(v ? '')
|
|
334
|
-
|
|
335
|
-
s.writeEndMarker()
|
|
336
|
-
|
|
337
|
-
# ==============================================================================
|
|
338
|
-
# Helper functions
|
|
339
|
-
# ==============================================================================
|
|
340
|
-
|
|
341
|
-
createValidityBitmap = (values) ->
|
|
342
|
-
byteCount = Math.ceil values.length / 8
|
|
343
|
-
bitmap = new Uint8Array byteCount
|
|
344
|
-
for v, i in values
|
|
345
|
-
if v? and v isnt null
|
|
346
|
-
byteIdx = Math.floor i / 8
|
|
347
|
-
bitIdx = i % 8
|
|
348
|
-
bitmap[byteIdx] |= 1 << bitIdx
|
|
349
|
-
bitmap
|
|
350
|
-
|
|
351
|
-
dateToDays = (value) ->
|
|
352
|
-
if value instanceof Date
|
|
353
|
-
Math.floor value.getTime() / (24 * 60 * 60 * 1000)
|
|
354
|
-
else if typeof value is 'string'
|
|
355
|
-
Math.floor new Date(value).getTime() / (24 * 60 * 60 * 1000)
|
|
356
|
-
else if typeof value is 'number'
|
|
357
|
-
value
|
|
358
|
-
else
|
|
359
|
-
0
|
|
360
|
-
|
|
361
|
-
timestampToMicros = (value) ->
|
|
362
|
-
if value instanceof Date
|
|
363
|
-
BigInt(value.getTime()) * 1000n
|
|
364
|
-
else if typeof value is 'string'
|
|
365
|
-
BigInt(new Date(value).getTime()) * 1000n
|
|
366
|
-
else if typeof value is 'number'
|
|
367
|
-
BigInt(value) * 1000n
|
|
368
|
-
else if typeof value is 'bigint'
|
|
369
|
-
value
|
|
370
|
-
else
|
|
371
|
-
0n
|
|
372
|
-
|
|
373
|
-
mapDuckDBType = (typeName) ->
|
|
374
|
-
return LogicalTypeId.VARCHAR unless typeName
|
|
375
|
-
upper = String(typeName).toUpperCase()
|
|
376
|
-
|
|
377
|
-
switch upper
|
|
378
|
-
when 'BOOLEAN', 'BOOL' then LogicalTypeId.BOOLEAN
|
|
379
|
-
when 'TINYINT', 'INT1' then LogicalTypeId.TINYINT
|
|
380
|
-
when 'SMALLINT', 'INT2' then LogicalTypeId.SMALLINT
|
|
381
|
-
when 'INTEGER', 'INT4', 'INT', 'SIGNED' then LogicalTypeId.INTEGER
|
|
382
|
-
when 'BIGINT', 'INT8', 'LONG' then LogicalTypeId.BIGINT
|
|
383
|
-
when 'UTINYINT' then LogicalTypeId.UTINYINT
|
|
384
|
-
when 'USMALLINT' then LogicalTypeId.USMALLINT
|
|
385
|
-
when 'UINTEGER', 'UINT' then LogicalTypeId.UINTEGER
|
|
386
|
-
when 'UBIGINT' then LogicalTypeId.UBIGINT
|
|
387
|
-
when 'HUGEINT' then LogicalTypeId.HUGEINT
|
|
388
|
-
when 'UHUGEINT' then LogicalTypeId.UHUGEINT
|
|
389
|
-
when 'FLOAT', 'FLOAT4', 'REAL' then LogicalTypeId.FLOAT
|
|
390
|
-
when 'DOUBLE', 'FLOAT8', 'NUMERIC' then LogicalTypeId.DOUBLE
|
|
391
|
-
when 'DATE' then LogicalTypeId.DATE
|
|
392
|
-
when 'TIME' then LogicalTypeId.TIME
|
|
393
|
-
when 'TIMESTAMP', 'DATETIME' then LogicalTypeId.TIMESTAMP
|
|
394
|
-
when 'TIMESTAMP WITH TIME ZONE', 'TIMESTAMPTZ' then LogicalTypeId.TIMESTAMP_TZ
|
|
395
|
-
when 'VARCHAR', 'TEXT', 'STRING', 'CHAR', 'BPCHAR' then LogicalTypeId.VARCHAR
|
|
396
|
-
when 'BLOB', 'BYTEA', 'BINARY', 'VARBINARY' then LogicalTypeId.BLOB
|
|
397
|
-
when 'UUID' then LogicalTypeId.UUID
|
|
398
|
-
when 'INTERVAL' then LogicalTypeId.INTERVAL
|
|
399
|
-
when 'JSON' then LogicalTypeId.VARCHAR
|
|
400
|
-
else
|
|
401
|
-
if upper.startsWith 'DECIMAL' then LogicalTypeId.DOUBLE
|
|
402
|
-
else if upper.startsWith 'VARCHAR' then LogicalTypeId.VARCHAR
|
|
403
|
-
else if upper.startsWith 'CHAR' then LogicalTypeId.CHAR
|
|
404
|
-
else LogicalTypeId.VARCHAR
|
|
405
|
-
|
|
406
|
-
# ==============================================================================
|
|
407
|
-
# SQL Tokenizer (for syntax highlighting)
|
|
408
|
-
# ==============================================================================
|
|
409
|
-
|
|
410
|
-
TokenType =
|
|
411
|
-
IDENTIFIER: 0
|
|
412
|
-
NUMERIC_CONSTANT: 1
|
|
413
|
-
STRING_CONSTANT: 2
|
|
414
|
-
OPERATOR: 3
|
|
415
|
-
KEYWORD: 4
|
|
416
|
-
COMMENT: 5
|
|
417
|
-
|
|
418
|
-
SQL_KEYWORDS = new Set [
|
|
419
|
-
'SELECT', 'FROM', 'WHERE', 'AND', 'OR', 'NOT', 'IN', 'IS', 'NULL',
|
|
420
|
-
'AS', 'ON', 'JOIN', 'LEFT', 'RIGHT', 'INNER', 'OUTER', 'FULL', 'CROSS',
|
|
421
|
-
'ORDER', 'BY', 'ASC', 'DESC', 'LIMIT', 'OFFSET', 'GROUP', 'HAVING',
|
|
422
|
-
'UNION', 'ALL', 'DISTINCT', 'INSERT', 'INTO', 'VALUES', 'UPDATE', 'SET',
|
|
423
|
-
'DELETE', 'CREATE', 'TABLE', 'INDEX', 'VIEW', 'DROP', 'ALTER', 'ADD',
|
|
424
|
-
'PRIMARY', 'KEY', 'FOREIGN', 'REFERENCES', 'CONSTRAINT', 'DEFAULT',
|
|
425
|
-
'CASE', 'WHEN', 'THEN', 'ELSE', 'END', 'CAST', 'TRUE', 'FALSE',
|
|
426
|
-
'WITH', 'RECURSIVE', 'OVER', 'PARTITION', 'WINDOW', 'ROWS',
|
|
427
|
-
'RANGE', 'BETWEEN', 'UNBOUNDED', 'PRECEDING', 'FOLLOWING', 'CURRENT',
|
|
428
|
-
'ROW', 'EXISTS', 'ANY', 'SOME', 'LIKE', 'ILIKE', 'SIMILAR', 'ESCAPE'
|
|
429
|
-
]
|
|
430
|
-
|
|
431
|
-
export tokenizeSQL = (sql) ->
|
|
432
|
-
tokens = []
|
|
433
|
-
i = 0
|
|
434
|
-
|
|
435
|
-
while i < sql.length
|
|
436
|
-
start = i
|
|
437
|
-
char = sql[i]
|
|
438
|
-
|
|
439
|
-
if /\s/.test char
|
|
440
|
-
i++
|
|
441
|
-
continue
|
|
442
|
-
|
|
443
|
-
if char is '-' and sql[i + 1] is '-'
|
|
444
|
-
i++ while i < sql.length and sql[i] isnt '\n'
|
|
445
|
-
tokens.push { offset: start, type: TokenType.COMMENT }
|
|
446
|
-
continue
|
|
447
|
-
|
|
448
|
-
if char is '/' and sql[i + 1] is '*'
|
|
449
|
-
i += 2
|
|
450
|
-
i++ while i < sql.length - 1 and not (sql[i] is '*' and sql[i + 1] is '/')
|
|
451
|
-
i += 2
|
|
452
|
-
tokens.push { offset: start, type: TokenType.COMMENT }
|
|
453
|
-
continue
|
|
454
|
-
|
|
455
|
-
if char is "'"
|
|
456
|
-
i++
|
|
457
|
-
while i < sql.length
|
|
458
|
-
if sql[i] is "'"
|
|
459
|
-
if sql[i + 1] is "'"
|
|
460
|
-
i += 2
|
|
461
|
-
else
|
|
462
|
-
i++
|
|
463
|
-
break
|
|
464
|
-
else
|
|
465
|
-
i++
|
|
466
|
-
tokens.push { offset: start, type: TokenType.STRING_CONSTANT }
|
|
467
|
-
continue
|
|
468
|
-
|
|
469
|
-
if char is '"'
|
|
470
|
-
i++
|
|
471
|
-
i++ while i < sql.length and sql[i] isnt '"'
|
|
472
|
-
i++ if i < sql.length
|
|
473
|
-
tokens.push { offset: start, type: TokenType.IDENTIFIER }
|
|
474
|
-
continue
|
|
475
|
-
|
|
476
|
-
if /[0-9]/.test(char) or (char is '.' and /[0-9]/.test(sql[i + 1] or ''))
|
|
477
|
-
i++ while i < sql.length and /[0-9.eE+-]/.test sql[i]
|
|
478
|
-
tokens.push { offset: start, type: TokenType.NUMERIC_CONSTANT }
|
|
479
|
-
continue
|
|
480
|
-
|
|
481
|
-
if /[a-zA-Z_]/.test char
|
|
482
|
-
i++ while i < sql.length and /[a-zA-Z0-9_]/.test sql[i]
|
|
483
|
-
word = sql.slice(start, i).toUpperCase()
|
|
484
|
-
type = if SQL_KEYWORDS.has word then TokenType.KEYWORD else TokenType.IDENTIFIER
|
|
485
|
-
tokens.push { offset: start, type }
|
|
486
|
-
continue
|
|
487
|
-
|
|
488
|
-
if /[+\-*/%=<>!&|^~]/.test char
|
|
489
|
-
if sql.slice(i, i + 2) in ['<=', '>=', '<>', '!=', '||', '&&', '::', '->']
|
|
490
|
-
i += 2
|
|
491
|
-
else i++
|
|
492
|
-
tokens.push { offset: start, type: TokenType.OPERATOR }
|
|
493
|
-
continue
|
|
494
|
-
|
|
495
|
-
if /[(),;.\[\]{}]/.test char
|
|
496
|
-
i++
|
|
497
|
-
tokens.push { offset: start, type: TokenType.OPERATOR }
|
|
498
|
-
continue
|
|
499
|
-
|
|
500
|
-
i++
|
|
501
|
-
|
|
502
|
-
tokens
|
|
503
|
-
|
|
504
|
-
# ==============================================================================
|
|
505
|
-
# Type inference from JavaScript values
|
|
506
|
-
# ==============================================================================
|
|
507
|
-
|
|
508
|
-
export inferType = (value) ->
|
|
509
|
-
return 'VARCHAR' if value is null or value is undefined
|
|
510
|
-
|
|
511
|
-
switch typeof value
|
|
512
|
-
when 'boolean' then 'BOOLEAN'
|
|
513
|
-
when 'number'
|
|
514
|
-
if Number.isInteger value
|
|
515
|
-
if value >= -2147483648 and value <= 2147483647 then 'INTEGER' else 'BIGINT'
|
|
516
|
-
else 'DOUBLE'
|
|
517
|
-
when 'bigint' then 'BIGINT'
|
|
518
|
-
when 'string'
|
|
519
|
-
if /^\d{4}-\d{2}-\d{2}$/.test value then 'DATE'
|
|
520
|
-
else if /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}/.test value then 'TIMESTAMP'
|
|
521
|
-
else 'VARCHAR'
|
|
522
|
-
when 'object'
|
|
523
|
-
if value instanceof Date then 'TIMESTAMP'
|
|
524
|
-
else 'VARCHAR'
|
|
525
|
-
else 'VARCHAR'
|