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
|
sqlmath/lib_lightgbm.dll
ADDED
|
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 | [](https://github.com/sqlmath/sqlmath/actions?query=branch%3Amaster) | [](https://github.com/sqlmath/sqlmath/actions?query=branch%3Abeta) | [](https://github.com/sqlmath/sqlmath/actions?query=branch%3Aalpha) |
|
|
33
|
+
| Coverage | [](https://sqlmath.github.io/sqlmath/branch-master/.artifact/coverage/index.html) | [](https://sqlmath.github.io/sqlmath/branch-beta/.artifact/coverage/index.html) | [](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
|
+
[](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
|
+
[](https://sqlmath.github.io/sqlmath/apidoc.html)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
<br><br>
|
|
119
|
+
# Package Listing
|
|
120
|
+

|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
<br><br>
|
|
124
|
+
# Changelog
|
|
125
|
+
- [Full CHANGELOG.md](CHANGELOG.md)
|
|
126
|
+
|
|
127
|
+

|
|
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,,
|