sqlmath 2025.8.30__cp314-cp314-win_amd64.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.
sqlmath/__init__.py ADDED
@@ -0,0 +1,642 @@
1
+ # Copyright (c) 2021 Kai Zhu
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ # SOFTWARE.
20
+
21
+
22
+ """sqlmath.py."""
23
+
24
+ __version__ = "2025.8.30"
25
+ __version_info__ = ("2025", "8", "30")
26
+
27
+ import json
28
+ import math
29
+ import re
30
+ import struct
31
+ import sys
32
+ import weakref
33
+
34
+ from ._sqlmath import _pybatonSetMemoryview, _pybatonStealCbuffer, _pydbCall
35
+
36
+ JSBATON_ARGC = 8
37
+ JSBATON_OFFSET_ALL = 256
38
+ JSBATON_OFFSET_ARGV = 128
39
+ JSBATON_OFFSET_FUNCNAME = 8
40
+ SIZEOF_BLOB_MAX = 1_000_000_000
41
+ SQLITE_DATATYPE_BLOB = 0x04
42
+ SQLITE_DATATYPE_EXTERNALBUFFER = 0x71
43
+ SQLITE_DATATYPE_FLOAT = 0x02
44
+ SQLITE_DATATYPE_INTEGER = 0x01
45
+ SQLITE_DATATYPE_INTEGER_0 = 0x00
46
+ SQLITE_DATATYPE_INTEGER_1 = 0x21
47
+ SQLITE_DATATYPE_NULL = 0x05
48
+ SQLITE_DATATYPE_TEXT = 0x03
49
+ SQLITE_DATATYPE_TEXT_0 = 0x13
50
+ SQLITE_RESPONSETYPE_LASTBLOB = 1
51
+
52
+
53
+ SQLITE_OPEN_AUTOPROXY = 0x00000020 # VFS only
54
+ SQLITE_OPEN_CREATE = 0x00000004 # Ok for sqlite3_open_v2()
55
+ SQLITE_OPEN_DELETEONCLOSE = 0x00000008 # VFS only
56
+ SQLITE_OPEN_EXCLUSIVE = 0x00000010 # VFS only
57
+ SQLITE_OPEN_FULLMUTEX = 0x00010000 # Ok for sqlite3_open_v2()
58
+ SQLITE_OPEN_MAIN_DB = 0x00000100 # VFS only
59
+ SQLITE_OPEN_MAIN_JOURNAL = 0x00000800 # VFS only
60
+ SQLITE_OPEN_MEMORY = 0x00000080 # Ok for sqlite3_open_v2()
61
+ SQLITE_OPEN_NOFOLLOW = 0x01000000 # Ok for sqlite3_open_v2()
62
+ SQLITE_OPEN_NOMUTEX = 0x00008000 # Ok for sqlite3_open_v2()
63
+ SQLITE_OPEN_PRIVATECACHE = 0x00040000 # Ok for sqlite3_open_v2()
64
+ SQLITE_OPEN_READONLY = 0x00000001 # Ok for sqlite3_open_v2()
65
+ SQLITE_OPEN_READWRITE = 0x00000002 # Ok for sqlite3_open_v2()
66
+ SQLITE_OPEN_SHAREDCACHE = 0x00020000 # Ok for sqlite3_open_v2()
67
+ SQLITE_OPEN_SUBJOURNAL = 0x00002000 # VFS only
68
+ SQLITE_OPEN_SUPER_JOURNAL = 0x00004000 # VFS only
69
+ SQLITE_OPEN_TEMP_DB = 0x00000200 # VFS only
70
+ SQLITE_OPEN_TEMP_JOURNAL = 0x00001000 # VFS only
71
+ SQLITE_OPEN_TRANSIENT_DB = 0x00000400 # VFS only
72
+ SQLITE_OPEN_URI = 0x00000040 # Ok for sqlite3_open_v2()
73
+ SQLITE_OPEN_WAL = 0x00080000 # VFS only
74
+
75
+
76
+ INFINITY = float("inf")
77
+ NAN = float("nan")
78
+
79
+
80
+ class SqlmathDb:
81
+ """Sqlmath database class."""
82
+
83
+ closed = False
84
+ filename = ""
85
+ ptr = 0
86
+
87
+
88
+ class SqlmathError(Exception):
89
+ """Sqlmath error."""
90
+
91
+
92
+ def assert_error_thrown(func, regexp):
93
+ """This function will assert calling <func> throws an error."""
94
+ err = None
95
+ try:
96
+ func()
97
+ except Exception as err_caught: # noqa: BLE001
98
+ err = err_caught
99
+ assert_or_throw(err, "No error thrown.")
100
+ assert_or_throw(not regexp or re.search(regexp, str(err)), err)
101
+
102
+
103
+ def assert_int64(val):
104
+ """This function will assert <val> is within range."""
105
+ """of c99-signed-long-long."""
106
+ assert_or_throw(
107
+ -9_223_372_036_854_775_808 <= val <= 9_223_372_036_854_775_807, # noqa: PLR2004
108
+ (
109
+ f"integer {val} outside signed-64-bit inclusive-range"
110
+ " -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807"
111
+ ),
112
+ )
113
+
114
+
115
+ def assert_json_equal(aa, bb, message=None):
116
+ """This function will assert."""
117
+ """JSON.stringify(<aa>) === JSON.stringify(<bb>)."""
118
+ aa = json.dumps(objectdeepcopywithkeyssorted(aa), indent=1)
119
+ bb = json.dumps(objectdeepcopywithkeyssorted(bb), indent=1)
120
+ if aa != bb:
121
+ raise SqlmathError(
122
+ f"\n{aa}\n!==\n{bb}"
123
+ + (
124
+ " - " + message
125
+ if isinstance(message, str)
126
+ else " - " + json.dumps(message)
127
+ if message
128
+ else ""
129
+ ),
130
+ )
131
+
132
+
133
+ def assert_or_throw(condition, message):
134
+ """This function will throw <message> if <condition> is falsy."""
135
+ if not condition:
136
+ raise (
137
+ SqlmathError(str(message)[:2048])
138
+ if not message or isinstance(message, str)
139
+ else message
140
+ )
141
+
142
+
143
+ def db_call(baton, arglist):
144
+ """This function will call c-function dbXxx() with given <funcname>."""
145
+ """and return [<baton>, ...arglist]."""
146
+ assert_or_throw(
147
+ len(arglist) <= JSBATON_ARGC,
148
+ f"db_call - len(arglist) must be less than than {JSBATON_ARGC}",
149
+ )
150
+ # serialize js-value to c-value
151
+ for argi, val in enumerate(arglist):
152
+ if val is None:
153
+ # ctype-q = long-long
154
+ struct.pack_into("q", baton, JSBATON_OFFSET_ARGV + argi * 8, 0)
155
+ continue
156
+ if isinstance(val, (bool, float, int)):
157
+ val2 = val
158
+ if isinstance(val, float):
159
+ assert_or_throw(
160
+ int(val) == val,
161
+ f"db_call - float {val} is not an integer",
162
+ )
163
+ val2 = int(val)
164
+ assert_int64(val2)
165
+ # ctype-q = long-long
166
+ struct.pack_into("q", baton, JSBATON_OFFSET_ARGV + argi * 8, val2)
167
+ continue
168
+ if isinstance(val, str):
169
+ baton = jsbaton_set_value(
170
+ baton,
171
+ argi,
172
+ val if val.endswith("\u0000") else val + "\u0000",
173
+ None,
174
+ None,
175
+ )
176
+ continue
177
+ msg = f'db_call - invalid arg-type "{type(val)}"'
178
+ raise SqlmathError(msg)
179
+ _pydbCall(baton)
180
+ return [baton]
181
+
182
+
183
+ def db_close(db):
184
+ """This function will close sqlite-database-connection <db>."""
185
+ # prevent segfault - do not close db if actions are pending
186
+ if not db.closed:
187
+ db.closed = True
188
+ db_call(jsbaton_create("_dbClose"), [db.ptr, db.filename])
189
+
190
+
191
+ def db_exec(
192
+ bind_list=None,
193
+ db=None,
194
+ response_type=None,
195
+ sql=None,
196
+ ):
197
+ """This function will exec <sql> in <db> and return <result>."""
198
+ if bind_list is None:
199
+ bind_list = []
200
+ baton = jsbaton_create("_dbExec")
201
+ bind_by_key = callable(getattr(bind_list, "items", None))
202
+ bufi = [0]
203
+ externalbuffer_list = []
204
+ if bind_by_key:
205
+ for key, val in bind_list.items():
206
+ baton = jsbaton_set_value(baton, None, f":{key}\u0000", None, None)
207
+ baton = jsbaton_set_value(
208
+ baton,
209
+ None,
210
+ val,
211
+ bufi,
212
+ externalbuffer_list,
213
+ )
214
+ else:
215
+ for val in bind_list:
216
+ baton = jsbaton_set_value(
217
+ baton,
218
+ None,
219
+ val,
220
+ bufi,
221
+ externalbuffer_list,
222
+ )
223
+ db_call(
224
+ baton,
225
+ [
226
+ # 0. db
227
+ db.ptr,
228
+ # 1. sql
229
+ str(sql) + "\n;\nPRAGMA noop",
230
+ # 2. len(bind_list)
231
+ len(bind_list),
232
+ # 3. bind_by_key
233
+ bind_by_key,
234
+ # 4. response_type
235
+ (
236
+ SQLITE_RESPONSETYPE_LASTBLOB
237
+ if response_type == "lastblob"
238
+ else 0
239
+ ),
240
+ ],
241
+ )
242
+ match response_type:
243
+ case "arraybuffer":
244
+ return memoryview(_pybatonStealCbuffer(baton, 0, 0))
245
+ case "lastblob":
246
+ return memoryview(_pybatonStealCbuffer(baton, 0, 0))
247
+ case "list":
248
+ return json.loads(_pybatonStealCbuffer(baton, 0, 1))
249
+ case _:
250
+ table_list = []
251
+ for table in json.loads(_pybatonStealCbuffer(baton, 0, 1)):
252
+ col_list = tuple(enumerate(table.pop(0)))
253
+ table_list.append([
254
+ {key: row[ii] for ii, key in col_list}
255
+ for row in table
256
+ ])
257
+ return table_list
258
+
259
+
260
+ def db_exec_and_return_lastblob(
261
+ bind_list=None,
262
+ db=None,
263
+ sql=None,
264
+ ):
265
+ """This function will exec <sql> in <db> and return last value."""
266
+ """ retrieved. from execution as raw blob/buffer."""
267
+ return db_exec(
268
+ bind_list=bind_list,
269
+ db=db,
270
+ response_type="lastblob",
271
+ sql=sql,
272
+ )
273
+
274
+
275
+ def db_file_load(
276
+ db=None,
277
+ filename=None,
278
+ mode_save=0,
279
+ ):
280
+ """This function will load <filename> to <db>."""
281
+ assert_or_throw(
282
+ isinstance(filename, str) and filename,
283
+ f"invalid filename {filename}",
284
+ )
285
+ db_call(
286
+ jsbaton_create("_dbFileLoad"),
287
+ [
288
+ # 0. sqlite3 * pInMemory
289
+ db.ptr,
290
+ # 1. char *zFilename
291
+ filename,
292
+ # 2. const int isSave
293
+ mode_save,
294
+ ],
295
+ )
296
+
297
+
298
+ def db_file_save(
299
+ db=None,
300
+ filename=None,
301
+ ):
302
+ """This function will save <db> to <filename>."""
303
+ return db_file_load(
304
+ db=db,
305
+ filename=filename,
306
+ mode_save=1,
307
+ )
308
+
309
+
310
+ def db_noop(*arglist):
311
+ """This function will do nothing except return <arglist>."""
312
+ return db_call(jsbaton_create("_dbNoop"), arglist)
313
+
314
+
315
+ def db_open(filename, flags=None):
316
+ """
317
+ This function will open and return sqlite-database-connection <db>.
318
+
319
+ int sqlite3_open_v2(
320
+ const char *filename, /* Database filename (UTF-8) */
321
+ sqlite3 **ppDb, /* OUT: SQLite db handle */
322
+ int flags, /* Flags */
323
+ const char *zVfs /* Name of VFS module to use */
324
+ )
325
+ """
326
+ assert_or_throw(isinstance(filename, str), f"invalid filename {filename}")
327
+ ptr = db_call(
328
+ jsbaton_create("_dbOpen"),
329
+ [
330
+ # 0. const char *filename, Database filename (UTF-8)
331
+ filename,
332
+ # 1. sqlite3 **ppDb, OUT: SQLite db handle
333
+ None,
334
+ # 2. int flags, Flags
335
+ (
336
+ SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI
337
+ if flags is None
338
+ else flags
339
+ ),
340
+ # 3. const char *zVfs Name of VFS module to use
341
+ None,
342
+ ],
343
+ )[0]
344
+ # ctype-q = long-long
345
+ ptr = struct.unpack_from("q", ptr, JSBATON_OFFSET_ARGV + 0)[0]
346
+ db = SqlmathDb()
347
+ db.filename = filename
348
+ db.ptr = ptr
349
+ weakref.finalize(db, db_close, db)
350
+ return db
351
+
352
+
353
+ def debuginline(*argv):
354
+ """This function will print <argv> to stderr and then return <argv>[0]."""
355
+ arg0 = argv[0] if argv else None
356
+ print("\n\ndebuginline", file=sys.stderr)
357
+ print(*argv, file=sys.stderr)
358
+ print("\n", file=sys.stderr)
359
+ return arg0
360
+
361
+
362
+ def jsbaton_create(funcname):
363
+ """This function will create buffer <baton>."""
364
+ baton = memoryview(bytearray(1024))
365
+ # init nalloc, nused
366
+ struct.pack_into("i", baton, 4, JSBATON_OFFSET_ALL) # ctype-i = int
367
+ # copy funcname into baton
368
+ funcname = bytes(funcname, "utf-8")
369
+ baton[
370
+ JSBATON_OFFSET_FUNCNAME: JSBATON_OFFSET_FUNCNAME + len(funcname)
371
+ ] = funcname
372
+ return baton
373
+
374
+
375
+ def jsbaton_get_int64(baton, argi):
376
+ """This function will return int64-value from <baton> at <argi>."""
377
+ # ctype-q = long-long
378
+ return struct.unpack_from("q", baton, JSBATON_OFFSET_ARGV + argi * 8)[0]
379
+
380
+
381
+ def jsbaton_get_string(baton, argi):
382
+ """This function will return string-value from <baton> at <argi>."""
383
+ # ctype-q = long-long
384
+ offset = struct.unpack_from("q", baton, JSBATON_OFFSET_ARGV + argi * 8)[0]
385
+ return str(
386
+ baton[
387
+ offset + 1 + 4:
388
+ # remove null-terminator from string
389
+ # ctype-i = int
390
+ offset + 1 + 4 + struct.unpack_from("i", baton, offset + 1)[0] - 1
391
+ ],
392
+ "utf-8",
393
+ )
394
+
395
+
396
+ def jsbaton_set_value(baton, argi, val, bufi, reference_list): # noqa: C901 PLR0912
397
+ """
398
+ This function will push <val> to buffer <baton>.
399
+
400
+ #define SQLITE_DATATYPE_BLOB 0x04
401
+ #define SQLITE_DATATYPE_EXTERNALBUFFER 0x71
402
+ #define SQLITE_DATATYPE_FLOAT 0x02
403
+ #define SQLITE_DATATYPE_INTEGER 0x01
404
+ #define SQLITE_DATATYPE_INTEGER_0 0x00
405
+ #define SQLITE_DATATYPE_INTEGER_1 0x21
406
+ #define SQLITE_DATATYPE_NULL 0x05
407
+ #define SQLITE_DATATYPE_TEXT 0x03
408
+ #define SQLITE_DATATYPE_TEXT_0 0x13
409
+
410
+ # 1. 0.NoneType
411
+ # 2. 0.bool
412
+ # 3. 0.bytearray
413
+ # 4. 0.bytes
414
+ # 5. 0.complex
415
+ # 6. 0.dict
416
+ # 7. 0.float
417
+ # 8. 0.frozenset
418
+ # 9. 0.int
419
+ # 10. 0.list
420
+ # 11. 0.memoryview
421
+ # 12. 0.range
422
+ # 13. 0.set
423
+ # 14. 0.str
424
+ # 15. 0.tuple
425
+ # 16. 1.NoneType
426
+ # 17. 1.bool
427
+ # 18. 1.bytearray
428
+ # 19. 1.bytes
429
+ # 20. 1.complex
430
+ # 21. 1.dict
431
+ # 22. 1.float
432
+ # 23. 1.frozenset
433
+ # 24. 1.int
434
+ # 25. 1.list
435
+ # 26. 1.memoryview
436
+ # 27. 1.range
437
+ # 28. 1.set
438
+ # 29. 1.str
439
+ # 20. 1.tuple
440
+ # 21. json
441
+
442
+ Format C Type Python type Standard size
443
+ x pad byte no value
444
+ c char byte 1
445
+ b signed char integer 1
446
+ B unsigned char integer 1
447
+ ? _Bool bool 1
448
+ h short integer 2
449
+ H unsigned short integer 2
450
+ i int integer 4
451
+ I unsigned int integer 4
452
+ l long integer 4
453
+ L unsigned long integer 4
454
+ q long long integer 8
455
+ Q unsigned long long integer 8
456
+ n ssize_t integer
457
+ N size_t integer
458
+ e half float 2
459
+ f float float 4
460
+ d double float 8
461
+ s char[] bytes
462
+ p char[] bytes
463
+ P void* integer
464
+ """
465
+ # 17. 1.bool
466
+ if val == 1:
467
+ val = True
468
+ match ("1." if val else "0.") + type(val).__name__:
469
+ # 1. 0.NoneType
470
+ case "0.NoneType":
471
+ vtype = SQLITE_DATATYPE_NULL
472
+ vsize = 0
473
+ # 2. 0.bool
474
+ case "0.bool":
475
+ vtype = SQLITE_DATATYPE_INTEGER_0
476
+ vsize = 0
477
+ # 3. 0.bytearray
478
+ case "0.bytearray":
479
+ vtype = SQLITE_DATATYPE_NULL
480
+ vsize = 0
481
+ # 4. 0.bytes
482
+ case "0.bytes":
483
+ vtype = SQLITE_DATATYPE_NULL
484
+ vsize = 0
485
+ # 5. 0.complex
486
+ # 6. 0.dict
487
+ # 7. 0.float
488
+ case "0.float":
489
+ vtype = SQLITE_DATATYPE_INTEGER_0
490
+ vsize = 0
491
+ # 8. 0.frozenset
492
+ # 9. 0.int
493
+ case "0.int":
494
+ vtype = SQLITE_DATATYPE_INTEGER_0
495
+ vsize = 0
496
+ # 10. 0.list
497
+ # 11. 0.memoryview
498
+ case "0.memoryview":
499
+ vtype = SQLITE_DATATYPE_NULL
500
+ vsize = 0
501
+ # 12. 0.range
502
+ # 13. 0.set
503
+ # 14. 0.str
504
+ case "0.str":
505
+ vtype = SQLITE_DATATYPE_TEXT_0
506
+ vsize = 0
507
+ # 15. 0.tuple
508
+ # 16. 1.NoneType
509
+ # 17. 1.bool
510
+ case "1.bool":
511
+ vtype = SQLITE_DATATYPE_INTEGER_1
512
+ vsize = 0
513
+ # 18. 1.bytearray
514
+ case "1.bytearray":
515
+ vtype = SQLITE_DATATYPE_BLOB
516
+ vsize = 4 + len(val)
517
+ # 19. 1.bytes
518
+ case "1.bytes":
519
+ vtype = SQLITE_DATATYPE_BLOB
520
+ vsize = 4 + len(val)
521
+ # 20. 1.complex
522
+ # 21. 1.dict
523
+ # 22. 1.float
524
+ case "1.float":
525
+ if math.isnan(val):
526
+ vtype = SQLITE_DATATYPE_NULL
527
+ vsize = 0
528
+ else:
529
+ vtype = SQLITE_DATATYPE_FLOAT
530
+ vsize = 8
531
+ # 23. 1.frozenset
532
+ # 24. 1.int
533
+ case "1.int":
534
+ vtype = SQLITE_DATATYPE_INTEGER
535
+ vsize = 8
536
+ # 25. 1.list
537
+ # 26. 1.memoryview
538
+ case "1.memoryview":
539
+ vtype = SQLITE_DATATYPE_EXTERNALBUFFER
540
+ vsize = 4
541
+ # 27. 1.range
542
+ # 28. 1.set
543
+ # 29. 1.str
544
+ case "1.str":
545
+ val = bytes(val, "utf-8")
546
+ vtype = SQLITE_DATATYPE_TEXT
547
+ vsize = 4 + len(val)
548
+ # 30. 1.tuple
549
+ # 31. json
550
+ case _:
551
+ val = bytes(json.dumps(val), "utf-8")
552
+ vtype = SQLITE_DATATYPE_TEXT
553
+ vsize = 4 + len(val)
554
+ nused = struct.unpack_from("i", baton, 4)[0] # ctype-i = int
555
+ nn = nused + 1 + vsize
556
+ assert_or_throw(
557
+ nn <= 0xffff_ffff, # noqa: PLR2004
558
+ "jsbaton cannot exceed 0x7fff_ffff / 2,147,483,647 bytes",
559
+ )
560
+ # exponentially grow baton as needed
561
+ if len(baton) < nn:
562
+ tmp = baton
563
+ baton = memoryview(
564
+ bytearray(min(2 ** math.ceil(math.log2(nn)), 0x7fff_ffff)),
565
+ )
566
+ # update nallc
567
+ struct.pack_into("i", baton, 0, len(baton)) # ctype-i = int
568
+ # copy old-baton into new-baton
569
+ baton[:len(tmp)] = tmp
570
+ # push vtype - 1-byte
571
+ struct.pack_into("b", baton, nused, vtype) # ctype-b = signed-char
572
+ # update nused
573
+ struct.pack_into("i", baton, 4, nused + 1 + vsize) # ctype-i = int
574
+ # handle blob-value
575
+ if vtype in (SQLITE_DATATYPE_BLOB, SQLITE_DATATYPE_TEXT):
576
+ # set argv[ii] to blob/text location
577
+ if argi is not None:
578
+ # ctype-i = int
579
+ struct.pack_into("i", baton, JSBATON_OFFSET_ARGV + argi * 8, nused)
580
+ vsize -= 4
581
+ assert_or_throw(
582
+ 0 <= vsize <= SIZEOF_BLOB_MAX,
583
+ (
584
+ "sqlite-blob byte-length must be within inclusive-range"
585
+ f" 0 to {SIZEOF_BLOB_MAX}"
586
+ ),
587
+ )
588
+ # push vsize - 4-byte
589
+ struct.pack_into("i", baton, nused + 1, vsize) # ctype-i = int
590
+ # push SQLITE-BLOB/TEXT - vsize-byte
591
+ baton[nused + 1 + 4:nused + 1 + 4 + vsize] = val
592
+ return baton
593
+ if vtype == SQLITE_DATATYPE_EXTERNALBUFFER:
594
+ vsize = len(val)
595
+ assert_or_throw(
596
+ 0 <= vsize <= SIZEOF_BLOB_MAX,
597
+ (
598
+ "sqlite-blob byte-length must be within inclusive-range"
599
+ f" 0 to {SIZEOF_BLOB_MAX}"
600
+ ),
601
+ )
602
+ assert_or_throw(
603
+ bufi[0] < JSBATON_ARGC,
604
+ f"cannot pass more than {JSBATON_ARGC} arraybuffers",
605
+ )
606
+ # push externalbuffer - 4-byte
607
+ struct.pack_into("i", baton, nused + 1, bufi[0]) # ctype-i = int
608
+ # set buffer
609
+ _pybatonSetMemoryview(baton, bufi[0], val)
610
+ # increment bufi
611
+ bufi[0] += 1
612
+ # add buffer to reference_list to prevent gc during db_call.
613
+ reference_list.append(val)
614
+ return baton
615
+ if vtype == SQLITE_DATATYPE_FLOAT:
616
+ # push SQLITE-REAL - 8-byte
617
+ struct.pack_into("d", baton, nused + 1, val) # ctype-d = double
618
+ return baton
619
+ if vtype == SQLITE_DATATYPE_INTEGER:
620
+ assert_int64(val)
621
+ # push SQLITE-INTEGER - 8-byte
622
+ struct.pack_into("q", baton, nused + 1, val) # ctype-q = long-long
623
+ return baton
624
+ return baton
625
+
626
+
627
+ def noop(val=None, *_, **__):
628
+ """This function will do nothing except return <val>."""
629
+ return val
630
+
631
+
632
+ def objectdeepcopywithkeyssorted(obj):
633
+ """This function will recursively deep-copy <obj> with keys sorted."""
634
+ if isinstance(obj, float):
635
+ return int(obj) if int(obj) == obj else obj
636
+ if not obj or not isinstance(obj, dict):
637
+ return obj
638
+ # Recursively deep-copy list with child-keys sorted.
639
+ if isinstance(obj, list):
640
+ return [objectdeepcopywithkeyssorted(elem) for elem in obj]
641
+ # Recursively deep-copy obj with keys sorted.
642
+ return dict(sorted((str(key), val) for key, val in obj.items()))
Binary file
Binary file
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2021 Kai Zhu
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.1
2
+ Name: sqlmath
3
+ Version: 2025.8.30
4
+ Summary: sqlite for datascience
5
+ Author: Kai Zhu
6
+ Requires-Python: >=3.10
7
+ License: MIT
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: C
16
+ Classifier: Programming Language :: JavaScript
17
+ Classifier: Programming Language :: Python
18
+ Classifier: Topic :: Database
19
+ Classifier: Topic :: Scientific/Engineering
20
+ Classifier: Topic :: Software Development
21
+ Project-URL: Homepage, https://github.com/sqlmath/sqlmath
22
+ Project-URL: Changelog, https://github.com/sqlmath/sqlmath/blob/master/CHANGELOG.md
23
+ License-File: LICENSE
24
+ Description-Content-Type: text/markdown
25
+
26
+ # sqlmath - sqlite for data-science
27
+
28
+
29
+ # Status
30
+ | Branch | [master<br>(v2025.8.30)](https://github.com/sqlmath/sqlmath/tree/master) | [beta<br>(Web Demo)](https://github.com/sqlmath/sqlmath/tree/beta) | [alpha<br>(Development)](https://github.com/sqlmath/sqlmath/tree/alpha) |
31
+ |--:|:--:|:--:|:--:|
32
+ | CI | [![ci](https://github.com/sqlmath/sqlmath/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/sqlmath/sqlmath/actions?query=branch%3Amaster) | [![ci](https://github.com/sqlmath/sqlmath/actions/workflows/ci.yml/badge.svg?branch=beta)](https://github.com/sqlmath/sqlmath/actions?query=branch%3Abeta) | [![ci](https://github.com/sqlmath/sqlmath/actions/workflows/ci.yml/badge.svg?branch=alpha)](https://github.com/sqlmath/sqlmath/actions?query=branch%3Aalpha) |
33
+ | Coverage | [![coverage](https://sqlmath.github.io/sqlmath/branch-master/.artifact/coverage/coverage_badge.svg)](https://sqlmath.github.io/sqlmath/branch-master/.artifact/coverage/index.html) | [![coverage](https://sqlmath.github.io/sqlmath/branch-beta/.artifact/coverage/coverage_badge.svg)](https://sqlmath.github.io/sqlmath/branch-beta/.artifact/coverage/index.html) | [![coverage](https://sqlmath.github.io/sqlmath/branch-alpha/.artifact/coverage/coverage_badge.svg)](https://sqlmath.github.io/sqlmath/branch-alpha/.artifact/coverage/index.html) |
34
+ | Demo | [<img src="https://sqlmath.github.io/sqlmath/asset_image_github_brands.svg" height="32">](https://sqlmath.github.io/sqlmath/branch-master/index.html) | [<img src="https://sqlmath.github.io/sqlmath/asset_image_github_brands.svg" height="32">](https://sqlmath.github.io/sqlmath/branch-beta/index.html) | [<img src="https://sqlmath.github.io/sqlmath/asset_image_github_brands.svg" height="32">](https://sqlmath.github.io/sqlmath/branch-alpha/index.html) |
35
+ | Artifacts | [<img src="https://sqlmath.github.io/sqlmath/asset_image_folder_open_solid.svg" height="30">](https://github.com/sqlmath/sqlmath/tree/gh-pages/branch-master/.artifact) | [<img src="https://sqlmath.github.io/sqlmath/asset_image_folder_open_solid.svg" height="30">](https://github.com/sqlmath/sqlmath/tree/gh-pages/branch-beta/.artifact) | [<img src="https://sqlmath.github.io/sqlmath/asset_image_folder_open_solid.svg" height="30">](https://github.com/sqlmath/sqlmath/tree/gh-pages/branch-alpha/.artifact) |
36
+
37
+
38
+ <br><br>
39
+ # Table of Contents
40
+
41
+ 1. [Web Demo](#web-demo)
42
+
43
+ 2. [Quickstart Build](#quickstart-build)
44
+ - [To build sqlmath:](#to-build-sqlmath)
45
+
46
+ 3. [Quickstart Website](#quickstart-website)
47
+ - [To serve your own sqlmath website:](#to-serve-your-own-sqlmath-website)
48
+
49
+ 4. [Documentation](#documentation)
50
+ - [API Doc](#api-doc)
51
+
52
+ 5. [Package Listing](#package-listing)
53
+
54
+ 6. [Changelog](#changelog)
55
+
56
+ 7. [License](#license)
57
+
58
+ 8. [Devops Instruction](#devops-instruction)
59
+ - [python pypi publish](#python-pypi-publish)
60
+ - [sqlite upgrade](#sqlite-upgrade)
61
+
62
+
63
+ <br><br>
64
+ # Web Demo
65
+ - https://sqlmath.github.io/sqlmath/index.html
66
+
67
+ [![screenshot](https://sqlmath.github.io/sqlmath/branch-beta/.artifact/screenshot_browser__2fsqlmath_2fbranch-beta_2findex.html.png)](https://sqlmath.github.io/sqlmath/index.html)
68
+
69
+
70
+ <br><br>
71
+ # Quickstart Build
72
+
73
+
74
+ <br><br>
75
+ ### To build sqlmath:
76
+ ```shell
77
+ #!/bin/sh
78
+
79
+ # git clone sqlmath repo
80
+ git clone https://github.com/sqlmath/sqlmath --branch=beta --single-branch
81
+ cd sqlmath
82
+
83
+ # build nodejs binary ./_binary_sqlmath_napi8_xxx_x64.node
84
+ npm run test2
85
+
86
+ # build webassembly binary ./sqlmath_wasm.wasm
87
+ sh jslint_ci.sh shCiBuildWasm
88
+ ```
89
+
90
+
91
+ <br><br>
92
+ # Quickstart Website
93
+
94
+
95
+ <br><br>
96
+ ### To serve your own sqlmath website:
97
+ ```shell
98
+ #!/bin/sh
99
+
100
+ # cd <sqlmath repo>
101
+
102
+ # serve website at http://localhost:8080/index.html
103
+ PORT=8080 sh jslint_ci.sh shHttpFileServer
104
+ ```
105
+
106
+
107
+ <br><br>
108
+ # Documentation
109
+
110
+
111
+ <br><br>
112
+ ### API Doc
113
+ - https://sqlmath.github.io/sqlmath/apidoc.html
114
+
115
+ [![screenshot](https://sqlmath.github.io/sqlmath/branch-beta/.artifact/screenshot_browser__2f.artifact_2fapidoc.html.png)](https://sqlmath.github.io/sqlmath/apidoc.html)
116
+
117
+
118
+ <br><br>
119
+ # Package Listing
120
+ ![screenshot_package_listing.svg](https://sqlmath.github.io/sqlmath/branch-beta/.artifact/screenshot_package_listing.svg)
121
+
122
+
123
+ <br><br>
124
+ # Changelog
125
+ - [Full CHANGELOG.md](CHANGELOG.md)
126
+
127
+ ![screenshot_changelog.svg](https://sqlmath.github.io/sqlmath/branch-beta/.artifact/screenshot_changelog.svg)
128
+
129
+
130
+ <br><br>
131
+ # License
132
+ - [sqlite](https://github.com/sqlite/sqlite) is under [public domain](https://www.sqlite.org/copyright.html).
133
+ - [jslint](https://github.com/jslint-org/jslint) is under [Unlicense License](https://github.com/jslint-org/jslint/blob/master/LICENSE).
134
+ - [zlib](https://github.com/madler/zlib) is under [zlib License](https://github.com/madler/zlib/blob/v1.2.13/LICENSE).
135
+ - [cpplint.py](cpplint.py) is under [3-Clause BSD License](https://github.com/cpplint/cpplint/blob/2.0.0/LICENSE).
136
+ - [indent.exe](indent.exe) is under [GPLv3 License](https://www.gnu.org/licenses/gpl-3.0.txt)<!--no-validate-->.
137
+ - Everything else is under MIT License.
138
+
139
+
140
+ <br><br>
141
+ # Devops Instruction
142
+
143
+
144
+ <br><br>
145
+ ### python pypi publish
146
+ ```shell
147
+ python -m build
148
+ #
149
+ twine upload --repository testpypi dist/sqlmath-2025.8.30*
150
+ py -m pip install --index-url https://test.pypi.org/simple/ sqlmath==2025.8.30
151
+ #
152
+ twine upload dist/sqlmath-2025.8.30*
153
+ pip install sqlmath==2025.8.30
154
+ ```
155
+
156
+
157
+ <br><br>
158
+ ### sqlite upgrade
159
+ - goto https://www.sqlite.org/changes.html
160
+ ```shell
161
+ (set -e
162
+ #
163
+ # lgbm
164
+ sh jslint_ci.sh shRollupUpgrade "v4.5.0" "v4.6.0" ".ci.sh sqlmath_base.h"
165
+ #
166
+ # sqlite
167
+ sh jslint_ci.sh shRollupUpgrade "3.50.3" "3.50.4" ".ci.sh sqlmath_external_sqlite.c"
168
+ sh jslint_ci.sh shRollupUpgrade "3500300" "3500400" ".ci.sh sqlmath_external_sqlite.c"
169
+ #
170
+ # zlib
171
+ # sh jslint_ci.sh shRollupUpgrade "1.3" "1.3.1" ".ci.sh sqlmath_external_zlib.c"
172
+ #
173
+ # shSqlmathUpdate
174
+ read -p "Press Enter to shSqlmathUpdate:"
175
+ sh jslint_ci.sh shSqlmathUpdate
176
+ )
177
+ ```
@@ -0,0 +1,7 @@
1
+ sqlmath/__init__.py,sha256=7w6Owk9Me8QmNZQ4QMe8ZislgUx406BLXBZlHFf_WYQ,20000
2
+ sqlmath/lib_lightgbm.dll,sha256=0qQVZ-FrEnskhrQXzCWBNYj-AVoEgeBv6M5mLr7R7GE,4059648
3
+ sqlmath/_sqlmath.cp314-win_amd64.pyd,sha256=1BUgcKBfnYnBb6IsEL8rKHGYk8WgA0YY7pU4BQVwFzg,1932288
4
+ sqlmath-2025.8.30.dist-info/LICENSE,sha256=KhokKrD65FnQuqeH79rJDSG_VdYZxu6rNKSHSAxpbYI,1051
5
+ sqlmath-2025.8.30.dist-info/METADATA,sha256=kK0da2OTgsNxGx8N8_Pn4AuC3tWJG96d4MRGX53bMOQ,6774
6
+ sqlmath-2025.8.30.dist-info/WHEEL,sha256=xprUZ6cvcNG8HucgHzjt9fcvB-Gk3VzSAhpyFkPj3yA,96
7
+ sqlmath-2025.8.30.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel 1.0
3
+ Root-Is-Purelib: false
4
+ Tag: cp314-cp314-win_amd64