singlestoredb 1.12.3__tar.gz → 1.13.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of singlestoredb might be problematic. Click here for more details.
- {singlestoredb-1.12.3/singlestoredb.egg-info → singlestoredb-1.13.0}/PKG-INFO +1 -1
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/accel.c +201 -37
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/setup.cfg +2 -1
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/__init__.py +1 -1
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/apps/__init__.py +1 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/apps/_config.py +6 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/apps/_connection_info.py +8 -0
- singlestoredb-1.13.0/singlestoredb/apps/_python_udfs.py +85 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/config.py +14 -2
- singlestoredb-1.13.0/singlestoredb/functions/__init__.py +12 -0
- singlestoredb-1.13.0/singlestoredb/functions/decorator.py +181 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/functions/dtypes.py +545 -198
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/functions/ext/asgi.py +288 -90
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/functions/ext/json.py +29 -36
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/functions/ext/mmap.py +1 -1
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/functions/ext/rowdat_1.py +50 -70
- singlestoredb-1.13.0/singlestoredb/functions/signature.py +1445 -0
- singlestoredb-1.13.0/singlestoredb/functions/typing.py +41 -0
- singlestoredb-1.13.0/singlestoredb/functions/utils.py +342 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/http/connection.py +3 -1
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/management/manager.py +6 -1
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/management/utils.py +2 -2
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/connection.py +17 -11
- singlestoredb-1.13.0/singlestoredb/tests/ext_funcs/__init__.py +624 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_basics.py +2 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_ext_func.py +192 -3
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_udf.py +101 -131
- singlestoredb-1.13.0/singlestoredb/tests/test_udf_returns.py +459 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0/singlestoredb.egg-info}/PKG-INFO +1 -1
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb.egg-info/SOURCES.txt +4 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb.egg-info/requires.txt +1 -0
- singlestoredb-1.12.3/singlestoredb/functions/__init__.py +0 -2
- singlestoredb-1.12.3/singlestoredb/functions/decorator.py +0 -331
- singlestoredb-1.12.3/singlestoredb/functions/signature.py +0 -773
- singlestoredb-1.12.3/singlestoredb/tests/ext_funcs/__init__.py +0 -385
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/LICENSE +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/README.md +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/setup.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/ai/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/ai/embeddings.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/alchemy/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/apps/_cloud_functions.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/apps/_dashboards.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/apps/_process.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/apps/_stdout_supress.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/apps/_uvicorn_util.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/auth.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/connection.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/converters.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/exceptions.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/functions/ext/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/functions/ext/arrow.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/functions/ext/utils.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/fusion/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/fusion/graphql.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/fusion/handler.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/fusion/handlers/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/fusion/handlers/export.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/fusion/handlers/files.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/fusion/handlers/job.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/fusion/handlers/models.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/fusion/handlers/stage.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/fusion/handlers/utils.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/fusion/handlers/workspace.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/fusion/registry.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/fusion/result.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/http/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/magics/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/magics/run_personal.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/magics/run_shared.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/management/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/management/billing_usage.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/management/cluster.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/management/export.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/management/files.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/management/job.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/management/organization.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/management/region.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/management/workspace.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/_auth.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/charset.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/constants/CLIENT.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/constants/COMMAND.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/constants/CR.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/constants/ER.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/constants/EXTENDED_TYPE.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/constants/FIELD_TYPE.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/constants/FLAG.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/constants/SERVER_STATUS.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/constants/VECTOR_TYPE.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/constants/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/converters.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/cursors.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/err.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/optionfile.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/protocol.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/base.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/conftest.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/test_DictCursor.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/test_SSCursor.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/test_basic.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/test_connection.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/test_converters.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/test_cursor.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/test_err.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/test_issues.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/test_load_local.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/test_nextset.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/test_optionfile.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/thirdparty/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/mysql/times.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/notebook/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/notebook/_objects.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/notebook/_portal.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/py.typed +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/pytest.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/server/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/server/docker.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/server/free_tier.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/empty.sql +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/local_infile.csv +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test.ipynb +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test.sql +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test2.ipynb +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test2.sql +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_config.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_connection.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_dbapi.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_exceptions.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_ext_func_data.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_fusion.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_http.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_management.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_plugin.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_results.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_types.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/test_xdict.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/tests/utils.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/types.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/utils/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/utils/config.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/utils/convert_rows.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/utils/debug.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/utils/dtypes.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/utils/events.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/utils/mogrify.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/utils/results.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb/utils/xdict.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb.egg-info/dependency_links.txt +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb.egg-info/entry_points.txt +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/singlestoredb.egg-info/top_level.txt +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/sqlx/__init__.py +0 -0
- {singlestoredb-1.12.3 → singlestoredb-1.13.0}/sqlx/magic.py +0 -0
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
#define NUMPY_TIMEDELTA 12
|
|
36
36
|
#define NUMPY_DATETIME 13
|
|
37
37
|
#define NUMPY_OBJECT 14
|
|
38
|
+
#define NUMPY_BYTES 15
|
|
39
|
+
#define NUMPY_FIXED_STRING 16
|
|
38
40
|
|
|
39
41
|
#define MYSQL_FLAG_NOT_NULL 1
|
|
40
42
|
#define MYSQL_FLAG_PRI_KEY 2
|
|
@@ -339,6 +341,11 @@
|
|
|
339
341
|
|
|
340
342
|
#define CHECKRC(x) if ((x) < 0) goto error;
|
|
341
343
|
|
|
344
|
+
typedef struct {
|
|
345
|
+
int type;
|
|
346
|
+
Py_ssize_t length;
|
|
347
|
+
} NumpyColType;
|
|
348
|
+
|
|
342
349
|
typedef struct {
|
|
343
350
|
int results_type;
|
|
344
351
|
int parse_json;
|
|
@@ -365,6 +372,83 @@ char *_PyUnicode_AsUTF8(PyObject *unicode) {
|
|
|
365
372
|
return out;
|
|
366
373
|
}
|
|
367
374
|
|
|
375
|
+
// Function to convert a UCS-4 string to a UTF-8 string
|
|
376
|
+
// Returns the length of the resulting UTF-8 string, or -1 on error
|
|
377
|
+
int ucs4_to_utf8(const uint32_t *ucs4_str, size_t ucs4_len, char **utf8_str) {
|
|
378
|
+
if (!ucs4_str || !utf8_str) {
|
|
379
|
+
return -1; // Invalid input
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Allocate a buffer for the UTF-8 string (worst-case: 4 bytes per UCS-4 character)
|
|
383
|
+
size_t utf8_max_len = ucs4_len * 4 + 1; // +1 for null terminator
|
|
384
|
+
*utf8_str = malloc(utf8_max_len);
|
|
385
|
+
if (!*utf8_str) {
|
|
386
|
+
return -1; // Memory allocation failed
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
char *utf8_ptr = *utf8_str;
|
|
390
|
+
size_t utf8_len = 0;
|
|
391
|
+
|
|
392
|
+
for (size_t i = 0; i < ucs4_len; i++) {
|
|
393
|
+
uint32_t codepoint = ucs4_str[i];
|
|
394
|
+
|
|
395
|
+
if (codepoint <= 0x7F) {
|
|
396
|
+
// 1-byte UTF-8
|
|
397
|
+
if (utf8_len + 1 > utf8_max_len) goto error; // Buffer overflow
|
|
398
|
+
*utf8_ptr++ = (char)codepoint;
|
|
399
|
+
utf8_len += 1;
|
|
400
|
+
} else if (codepoint <= 0x7FF) {
|
|
401
|
+
// 2-byte UTF-8
|
|
402
|
+
if (utf8_len + 2 > utf8_max_len) goto error; // Buffer overflow
|
|
403
|
+
*utf8_ptr++ = (char)(0xC0 | (codepoint >> 6));
|
|
404
|
+
*utf8_ptr++ = (char)(0x80 | (codepoint & 0x3F));
|
|
405
|
+
utf8_len += 2;
|
|
406
|
+
} else if (codepoint <= 0xFFFF) {
|
|
407
|
+
// 3-byte UTF-8
|
|
408
|
+
if (utf8_len + 3 > utf8_max_len) goto error; // Buffer overflow
|
|
409
|
+
*utf8_ptr++ = (char)(0xE0 | (codepoint >> 12));
|
|
410
|
+
*utf8_ptr++ = (char)(0x80 | ((codepoint >> 6) & 0x3F));
|
|
411
|
+
*utf8_ptr++ = (char)(0x80 | (codepoint & 0x3F));
|
|
412
|
+
utf8_len += 3;
|
|
413
|
+
} else if (codepoint <= 0x10FFFF) {
|
|
414
|
+
// 4-byte UTF-8
|
|
415
|
+
if (utf8_len + 4 > utf8_max_len) goto error; // Buffer overflow
|
|
416
|
+
*utf8_ptr++ = (char)(0xF0 | (codepoint >> 18));
|
|
417
|
+
*utf8_ptr++ = (char)(0x80 | ((codepoint >> 12) & 0x3F));
|
|
418
|
+
*utf8_ptr++ = (char)(0x80 | ((codepoint >> 6) & 0x3F));
|
|
419
|
+
*utf8_ptr++ = (char)(0x80 | (codepoint & 0x3F));
|
|
420
|
+
utf8_len += 4;
|
|
421
|
+
} else {
|
|
422
|
+
// Invalid codepoint
|
|
423
|
+
goto error;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Null-terminate the UTF-8 string
|
|
428
|
+
if (utf8_len + 1 > utf8_max_len) goto error; // Buffer overflow
|
|
429
|
+
*utf8_ptr = '\0';
|
|
430
|
+
|
|
431
|
+
return (int)utf8_len;
|
|
432
|
+
|
|
433
|
+
error:
|
|
434
|
+
free(*utf8_str);
|
|
435
|
+
*utf8_str = NULL;
|
|
436
|
+
return -1;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
size_t length_without_trailing_nulls(const char *str, size_t len) {
|
|
440
|
+
if (!str || len == 0) {
|
|
441
|
+
return 0; // Handle null or empty input
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Start from the end of the string and move backward
|
|
445
|
+
while (len > 0 && str[len - 1] == '\0') {
|
|
446
|
+
len--;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return len;
|
|
450
|
+
}
|
|
451
|
+
|
|
368
452
|
//
|
|
369
453
|
// Cached int values for date/time components
|
|
370
454
|
//
|
|
@@ -2646,8 +2730,8 @@ error:
|
|
|
2646
2730
|
}
|
|
2647
2731
|
|
|
2648
2732
|
|
|
2649
|
-
static
|
|
2650
|
-
|
|
2733
|
+
static NumpyColType get_numpy_col_type(PyObject *py_array) {
|
|
2734
|
+
NumpyColType out = {0};
|
|
2651
2735
|
char *str = NULL;
|
|
2652
2736
|
PyObject *py_array_interface = NULL;
|
|
2653
2737
|
PyObject *py_typestr = NULL;
|
|
@@ -2665,58 +2749,86 @@ static int get_numpy_col_type(PyObject *py_array) {
|
|
|
2665
2749
|
|
|
2666
2750
|
switch (str[1]) {
|
|
2667
2751
|
case 'b':
|
|
2668
|
-
out = NUMPY_BOOL;
|
|
2752
|
+
out.type = NUMPY_BOOL;
|
|
2753
|
+
out.length = 1;
|
|
2669
2754
|
break;
|
|
2670
2755
|
case 'i':
|
|
2671
2756
|
switch (str[2]) {
|
|
2672
2757
|
case '1':
|
|
2673
|
-
out = NUMPY_INT8;
|
|
2758
|
+
out.type = NUMPY_INT8;
|
|
2759
|
+
out.length = 1;
|
|
2674
2760
|
break;
|
|
2675
2761
|
case '2':
|
|
2676
|
-
out = NUMPY_INT16;
|
|
2762
|
+
out.type = NUMPY_INT16;
|
|
2763
|
+
out.length = 2;
|
|
2677
2764
|
break;
|
|
2678
2765
|
case '4':
|
|
2679
|
-
out = NUMPY_INT32;
|
|
2766
|
+
out.type = NUMPY_INT32;
|
|
2767
|
+
out.length = 4;
|
|
2680
2768
|
break;
|
|
2681
2769
|
case '8':
|
|
2682
|
-
out = NUMPY_INT64;
|
|
2770
|
+
out.type = NUMPY_INT64;
|
|
2771
|
+
out.length = 8;
|
|
2683
2772
|
break;
|
|
2684
2773
|
}
|
|
2685
2774
|
break;
|
|
2686
2775
|
case 'u':
|
|
2687
2776
|
switch (str[2]) {
|
|
2688
2777
|
case '1':
|
|
2689
|
-
out = NUMPY_UINT8;
|
|
2778
|
+
out.type = NUMPY_UINT8;
|
|
2779
|
+
out.length = 1;
|
|
2690
2780
|
break;
|
|
2691
2781
|
case '2':
|
|
2692
|
-
out = NUMPY_UINT16;
|
|
2782
|
+
out.type = NUMPY_UINT16;
|
|
2783
|
+
out.length = 2;
|
|
2693
2784
|
break;
|
|
2694
2785
|
case '4':
|
|
2695
|
-
out = NUMPY_UINT32;
|
|
2786
|
+
out.type = NUMPY_UINT32;
|
|
2787
|
+
out.length = 4;
|
|
2696
2788
|
break;
|
|
2697
2789
|
case '8':
|
|
2698
|
-
out = NUMPY_UINT64;
|
|
2790
|
+
out.type = NUMPY_UINT64;
|
|
2791
|
+
out.length = 8;
|
|
2699
2792
|
break;
|
|
2700
2793
|
}
|
|
2701
2794
|
break;
|
|
2702
2795
|
case 'f':
|
|
2703
2796
|
switch (str[2]) {
|
|
2704
2797
|
case '4':
|
|
2705
|
-
out = NUMPY_FLOAT32;
|
|
2798
|
+
out.type = NUMPY_FLOAT32;
|
|
2799
|
+
out.length = 4;
|
|
2706
2800
|
break;
|
|
2707
2801
|
case '8':
|
|
2708
|
-
out = NUMPY_FLOAT64;
|
|
2802
|
+
out.type = NUMPY_FLOAT64;
|
|
2803
|
+
out.length = 8;
|
|
2709
2804
|
break;
|
|
2710
2805
|
}
|
|
2711
2806
|
break;
|
|
2712
2807
|
case 'O':
|
|
2713
|
-
out = NUMPY_OBJECT;
|
|
2808
|
+
out.type = NUMPY_OBJECT;
|
|
2809
|
+
out.length = 8;
|
|
2714
2810
|
break;
|
|
2715
2811
|
case 'm':
|
|
2716
|
-
out = NUMPY_TIMEDELTA;
|
|
2812
|
+
out.type = NUMPY_TIMEDELTA;
|
|
2813
|
+
out.length = 8;
|
|
2717
2814
|
break;
|
|
2718
2815
|
case 'M':
|
|
2719
|
-
out = NUMPY_DATETIME;
|
|
2816
|
+
out.type = NUMPY_DATETIME;
|
|
2817
|
+
out.length = 8;
|
|
2818
|
+
break;
|
|
2819
|
+
case 'S':
|
|
2820
|
+
out.type = NUMPY_BYTES;
|
|
2821
|
+
out.length = (Py_ssize_t)strtol(str + 2, NULL, 10);
|
|
2822
|
+
if (out.length < 0) {
|
|
2823
|
+
goto error;
|
|
2824
|
+
}
|
|
2825
|
+
break;
|
|
2826
|
+
case 'U':
|
|
2827
|
+
out.type = NUMPY_FIXED_STRING;
|
|
2828
|
+
out.length = (Py_ssize_t)strtol(str + 2, NULL, 10);
|
|
2829
|
+
if (out.length < 0) {
|
|
2830
|
+
goto error;
|
|
2831
|
+
}
|
|
2720
2832
|
break;
|
|
2721
2833
|
default:
|
|
2722
2834
|
goto error;
|
|
@@ -2730,7 +2842,8 @@ exit:
|
|
|
2730
2842
|
return out;
|
|
2731
2843
|
|
|
2732
2844
|
error:
|
|
2733
|
-
out = 0;
|
|
2845
|
+
out.type = 0;
|
|
2846
|
+
out.length = 0;
|
|
2734
2847
|
goto exit;
|
|
2735
2848
|
}
|
|
2736
2849
|
|
|
@@ -2774,7 +2887,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
2774
2887
|
unsigned long long j = 0;
|
|
2775
2888
|
char **cols = NULL;
|
|
2776
2889
|
char **masks = NULL;
|
|
2777
|
-
|
|
2890
|
+
NumpyColType *col_types = NULL;
|
|
2778
2891
|
int64_t *row_ids = NULL;
|
|
2779
2892
|
|
|
2780
2893
|
// Parse function args.
|
|
@@ -2847,7 +2960,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
2847
2960
|
// Get column array memory
|
|
2848
2961
|
cols = calloc(sizeof(char*), n_cols);
|
|
2849
2962
|
if (!cols) goto error;
|
|
2850
|
-
col_types = calloc(sizeof(
|
|
2963
|
+
col_types = calloc(sizeof(NumpyColType), n_cols);
|
|
2851
2964
|
if (!col_types) goto error;
|
|
2852
2965
|
masks = calloc(sizeof(char*), n_cols);
|
|
2853
2966
|
if (!masks) goto error;
|
|
@@ -2865,7 +2978,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
2865
2978
|
}
|
|
2866
2979
|
|
|
2867
2980
|
col_types[i] = get_numpy_col_type(py_data);
|
|
2868
|
-
if (!col_types[i]) {
|
|
2981
|
+
if (!col_types[i].type) {
|
|
2869
2982
|
PyErr_SetString(PyExc_ValueError, "unable to get column type of data column");
|
|
2870
2983
|
goto error;
|
|
2871
2984
|
}
|
|
@@ -2874,7 +2987,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
2874
2987
|
if (!py_mask) goto error;
|
|
2875
2988
|
|
|
2876
2989
|
masks[i] = get_array_base_address(py_mask);
|
|
2877
|
-
if (masks[i] && get_numpy_col_type(py_mask) != NUMPY_BOOL) {
|
|
2990
|
+
if (masks[i] && get_numpy_col_type(py_mask).type != NUMPY_BOOL) {
|
|
2878
2991
|
PyErr_SetString(PyExc_ValueError, "mask must only contain boolean values");
|
|
2879
2992
|
goto error;
|
|
2880
2993
|
}
|
|
@@ -2958,7 +3071,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
2958
3071
|
|
|
2959
3072
|
case MYSQL_TYPE_TINY:
|
|
2960
3073
|
CHECKMEM(1);
|
|
2961
|
-
switch (col_types[i]) {
|
|
3074
|
+
switch (col_types[i].type) {
|
|
2962
3075
|
case NUMPY_BOOL:
|
|
2963
3076
|
i8 = *(int8_t*)(cols[i] + j * 1);
|
|
2964
3077
|
CHECK_TINYINT(i8, 0);
|
|
@@ -3025,7 +3138,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3025
3138
|
// Use negative to indicate unsigned
|
|
3026
3139
|
case -MYSQL_TYPE_TINY:
|
|
3027
3140
|
CHECKMEM(1);
|
|
3028
|
-
switch (col_types[i]) {
|
|
3141
|
+
switch (col_types[i].type) {
|
|
3029
3142
|
case NUMPY_BOOL:
|
|
3030
3143
|
i8 = *(int8_t*)(cols[i] + j * 1);
|
|
3031
3144
|
CHECK_UNSIGNED_TINYINT(i8, 0);
|
|
@@ -3091,7 +3204,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3091
3204
|
|
|
3092
3205
|
case MYSQL_TYPE_SHORT:
|
|
3093
3206
|
CHECKMEM(2);
|
|
3094
|
-
switch (col_types[i]) {
|
|
3207
|
+
switch (col_types[i].type) {
|
|
3095
3208
|
case NUMPY_BOOL:
|
|
3096
3209
|
i8 = *(int8_t*)(cols[i] + j * 1);
|
|
3097
3210
|
CHECK_SMALLINT(i8, 0);
|
|
@@ -3158,7 +3271,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3158
3271
|
// Use negative to indicate unsigned
|
|
3159
3272
|
case -MYSQL_TYPE_SHORT:
|
|
3160
3273
|
CHECKMEM(2);
|
|
3161
|
-
switch (col_types[i]) {
|
|
3274
|
+
switch (col_types[i].type) {
|
|
3162
3275
|
case NUMPY_BOOL:
|
|
3163
3276
|
i8 = *(int8_t*)(cols[i] + j * 1);
|
|
3164
3277
|
CHECK_UNSIGNED_SMALLINT(i8, 0);
|
|
@@ -3224,7 +3337,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3224
3337
|
|
|
3225
3338
|
case MYSQL_TYPE_INT24:
|
|
3226
3339
|
CHECKMEM(4);
|
|
3227
|
-
switch (col_types[i]) {
|
|
3340
|
+
switch (col_types[i].type) {
|
|
3228
3341
|
case NUMPY_BOOL:
|
|
3229
3342
|
i8 = *(int8_t*)(cols[i] + j * 1);
|
|
3230
3343
|
CHECK_MEDIUMINT(i8, 0);
|
|
@@ -3290,7 +3403,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3290
3403
|
|
|
3291
3404
|
case MYSQL_TYPE_LONG:
|
|
3292
3405
|
CHECKMEM(4);
|
|
3293
|
-
switch (col_types[i]) {
|
|
3406
|
+
switch (col_types[i].type) {
|
|
3294
3407
|
case NUMPY_BOOL:
|
|
3295
3408
|
i8 = *(int8_t*)(cols[i] + j * 1);
|
|
3296
3409
|
CHECK_INT(i8, 0);
|
|
@@ -3357,7 +3470,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3357
3470
|
// Use negative to indicate unsigned
|
|
3358
3471
|
case -MYSQL_TYPE_INT24:
|
|
3359
3472
|
CHECKMEM(4);
|
|
3360
|
-
switch (col_types[i]) {
|
|
3473
|
+
switch (col_types[i].type) {
|
|
3361
3474
|
case NUMPY_BOOL:
|
|
3362
3475
|
i8 = *(int8_t*)(cols[i] + j * 1);
|
|
3363
3476
|
CHECK_UNSIGNED_MEDIUMINT(i8, 0);
|
|
@@ -3424,7 +3537,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3424
3537
|
// Use negative to indicate unsigned
|
|
3425
3538
|
case -MYSQL_TYPE_LONG:
|
|
3426
3539
|
CHECKMEM(4);
|
|
3427
|
-
switch (col_types[i]) {
|
|
3540
|
+
switch (col_types[i].type) {
|
|
3428
3541
|
case NUMPY_BOOL:
|
|
3429
3542
|
i8 = *(int8_t*)(cols[i] + j * 1);
|
|
3430
3543
|
CHECK_UNSIGNED_INT(i8, 0);
|
|
@@ -3490,7 +3603,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3490
3603
|
|
|
3491
3604
|
case MYSQL_TYPE_LONGLONG:
|
|
3492
3605
|
CHECKMEM(8);
|
|
3493
|
-
switch (col_types[i]) {
|
|
3606
|
+
switch (col_types[i].type) {
|
|
3494
3607
|
case NUMPY_BOOL:
|
|
3495
3608
|
i8 = *(int8_t*)(cols[i] + j * 1);
|
|
3496
3609
|
CHECK_BIGINT(i8, 0);
|
|
@@ -3557,7 +3670,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3557
3670
|
// Use negative to indicate unsigned
|
|
3558
3671
|
case -MYSQL_TYPE_LONGLONG:
|
|
3559
3672
|
CHECKMEM(8);
|
|
3560
|
-
switch (col_types[i]) {
|
|
3673
|
+
switch (col_types[i].type) {
|
|
3561
3674
|
case NUMPY_BOOL:
|
|
3562
3675
|
i8 = *(int8_t*)(cols[i] + j * 1);
|
|
3563
3676
|
CHECK_UNSIGNED_BIGINT(i8, 0);
|
|
@@ -3623,7 +3736,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3623
3736
|
|
|
3624
3737
|
case MYSQL_TYPE_FLOAT:
|
|
3625
3738
|
CHECKMEM(4);
|
|
3626
|
-
switch (col_types[i]) {
|
|
3739
|
+
switch (col_types[i].type) {
|
|
3627
3740
|
case NUMPY_BOOL:
|
|
3628
3741
|
flt = (float)((is_null) ? 0 : *(int8_t*)(cols[i] + j * 1));
|
|
3629
3742
|
break;
|
|
@@ -3667,7 +3780,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3667
3780
|
|
|
3668
3781
|
case MYSQL_TYPE_DOUBLE:
|
|
3669
3782
|
CHECKMEM(8);
|
|
3670
|
-
switch (col_types[i]) {
|
|
3783
|
+
switch (col_types[i].type) {
|
|
3671
3784
|
case NUMPY_BOOL:
|
|
3672
3785
|
dbl = (double)((is_null) ? 0 : *(int8_t*)(cols[i] + j * 1));
|
|
3673
3786
|
break;
|
|
@@ -3742,7 +3855,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3742
3855
|
|
|
3743
3856
|
case MYSQL_TYPE_YEAR:
|
|
3744
3857
|
CHECKMEM(2);
|
|
3745
|
-
switch (col_types[i]) {
|
|
3858
|
+
switch (col_types[i].type) {
|
|
3746
3859
|
case NUMPY_BOOL:
|
|
3747
3860
|
i8 = *(int8_t*)(cols[i] + j * 1);
|
|
3748
3861
|
CHECK_YEAR(i8);
|
|
@@ -3817,7 +3930,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3817
3930
|
case MYSQL_TYPE_MEDIUM_BLOB:
|
|
3818
3931
|
case MYSQL_TYPE_LONG_BLOB:
|
|
3819
3932
|
case MYSQL_TYPE_BLOB:
|
|
3820
|
-
if (col_types[i] != NUMPY_OBJECT) {
|
|
3933
|
+
if (col_types[i].type != NUMPY_OBJECT && col_types[i].type != NUMPY_FIXED_STRING) {
|
|
3821
3934
|
PyErr_SetString(PyExc_ValueError, "unsupported numpy data type for character output types");
|
|
3822
3935
|
goto error;
|
|
3823
3936
|
}
|
|
@@ -3828,6 +3941,33 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3828
3941
|
memcpy(out+out_idx, &i64, 8);
|
|
3829
3942
|
out_idx += 8;
|
|
3830
3943
|
|
|
3944
|
+
} else if (col_types[i].type == NUMPY_FIXED_STRING) {
|
|
3945
|
+
// Jump to col_types[i].length * 4 for UCS4 fixed length string
|
|
3946
|
+
void *bytes = (void*)(cols[i] + j * col_types[i].length * 4);
|
|
3947
|
+
|
|
3948
|
+
if (bytes == NULL) {
|
|
3949
|
+
CHECKMEM(8);
|
|
3950
|
+
i64 = 0;
|
|
3951
|
+
memcpy(out+out_idx, &i64, 8);
|
|
3952
|
+
out_idx += 8;
|
|
3953
|
+
} else {
|
|
3954
|
+
char *utf8_str = NULL;
|
|
3955
|
+
Py_ssize_t str_l = ucs4_to_utf8(bytes, col_types[i].length, &utf8_str);
|
|
3956
|
+
if (str_l < 0) {
|
|
3957
|
+
PyErr_SetString(PyExc_ValueError, "invalid UCS4 string");
|
|
3958
|
+
if (utf8_str) free(utf8_str);
|
|
3959
|
+
goto error;
|
|
3960
|
+
}
|
|
3961
|
+
str_l = strnlen(utf8_str, str_l);
|
|
3962
|
+
CHECKMEM(8+str_l);
|
|
3963
|
+
i64 = str_l;
|
|
3964
|
+
memcpy(out+out_idx, &i64, 8);
|
|
3965
|
+
out_idx += 8;
|
|
3966
|
+
memcpy(out+out_idx, utf8_str, str_l);
|
|
3967
|
+
out_idx += str_l;
|
|
3968
|
+
free(utf8_str);
|
|
3969
|
+
}
|
|
3970
|
+
|
|
3831
3971
|
} else {
|
|
3832
3972
|
u64 = *(uint64_t*)(cols[i] + j * 8);
|
|
3833
3973
|
|
|
@@ -3873,7 +4013,7 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3873
4013
|
case -MYSQL_TYPE_MEDIUM_BLOB:
|
|
3874
4014
|
case -MYSQL_TYPE_LONG_BLOB:
|
|
3875
4015
|
case -MYSQL_TYPE_BLOB:
|
|
3876
|
-
if (col_types[i] != NUMPY_OBJECT) {
|
|
4016
|
+
if (col_types[i].type != NUMPY_OBJECT && col_types[i].type != NUMPY_BYTES) {
|
|
3877
4017
|
PyErr_SetString(PyExc_ValueError, "unsupported numpy data type for binary output types");
|
|
3878
4018
|
goto error;
|
|
3879
4019
|
}
|
|
@@ -3884,6 +4024,27 @@ static PyObject *dump_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
|
|
|
3884
4024
|
memcpy(out+out_idx, &i64, 8);
|
|
3885
4025
|
out_idx += 8;
|
|
3886
4026
|
|
|
4027
|
+
} else if (col_types[i].type == NUMPY_BYTES) {
|
|
4028
|
+
void *bytes = (void*)(cols[i] + j * col_types[i].length);
|
|
4029
|
+
|
|
4030
|
+
if (bytes == NULL) {
|
|
4031
|
+
CHECKMEM(8);
|
|
4032
|
+
i64 = 0;
|
|
4033
|
+
memcpy(out+out_idx, &i64, 8);
|
|
4034
|
+
out_idx += 8;
|
|
4035
|
+
} else {
|
|
4036
|
+
Py_ssize_t str_l = col_types[i].length;
|
|
4037
|
+
CHECKMEM(8+str_l);
|
|
4038
|
+
|
|
4039
|
+
str_l = length_without_trailing_nulls(bytes, str_l);
|
|
4040
|
+
|
|
4041
|
+
i64 = str_l;
|
|
4042
|
+
memcpy(out+out_idx, &i64, 8);
|
|
4043
|
+
out_idx += 8;
|
|
4044
|
+
memcpy(out+out_idx, bytes, str_l);
|
|
4045
|
+
out_idx += str_l;
|
|
4046
|
+
}
|
|
4047
|
+
|
|
3887
4048
|
} else {
|
|
3888
4049
|
u64 = *(uint64_t*)(cols[i] + j * 8);
|
|
3889
4050
|
|
|
@@ -4291,7 +4452,10 @@ static PyObject *dump_rowdat_1(PyObject *self, PyObject *args, PyObject *kwargs)
|
|
|
4291
4452
|
|
|
4292
4453
|
// Get return types
|
|
4293
4454
|
n_cols = (unsigned long long)PyObject_Length(py_returns);
|
|
4294
|
-
if (n_cols == 0)
|
|
4455
|
+
if (n_cols == 0) {
|
|
4456
|
+
PyErr_SetString(PyExc_ValueError, "no return values specified");
|
|
4457
|
+
goto error;
|
|
4458
|
+
}
|
|
4295
4459
|
|
|
4296
4460
|
returns = malloc(sizeof(int) * n_cols);
|
|
4297
4461
|
if (!returns) goto error;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[metadata]
|
|
2
2
|
name = singlestoredb
|
|
3
|
-
version = 1.
|
|
3
|
+
version = 1.13.0
|
|
4
4
|
description = Interface to the SingleStoreDB database and workspace management APIs
|
|
5
5
|
long_description = file: README.md
|
|
6
6
|
long_description_content_type = text/markdown
|
|
@@ -27,6 +27,7 @@ install_requires =
|
|
|
27
27
|
sqlparams
|
|
28
28
|
wheel
|
|
29
29
|
tomli>=1.1.0;python_version < '3.11'
|
|
30
|
+
typing-extensions<=4.13.2;python_version < '3.11'
|
|
30
31
|
python_requires = >=3.8
|
|
31
32
|
include_package_data = True
|
|
32
33
|
tests_require =
|
|
@@ -8,10 +8,12 @@ class AppConfig:
|
|
|
8
8
|
listen_port: int
|
|
9
9
|
base_url: str
|
|
10
10
|
base_path: str
|
|
11
|
+
notebook_server_id: str
|
|
11
12
|
app_token: Optional[str]
|
|
12
13
|
user_token: Optional[str]
|
|
13
14
|
running_interactively: bool
|
|
14
15
|
is_gateway_enabled: bool
|
|
16
|
+
is_local_dev: bool
|
|
15
17
|
|
|
16
18
|
@staticmethod
|
|
17
19
|
def _read_variable(name: str) -> str:
|
|
@@ -28,6 +30,8 @@ class AppConfig:
|
|
|
28
30
|
port = cls._read_variable('SINGLESTOREDB_APP_LISTEN_PORT')
|
|
29
31
|
base_url = cls._read_variable('SINGLESTOREDB_APP_BASE_URL')
|
|
30
32
|
base_path = cls._read_variable('SINGLESTOREDB_APP_BASE_PATH')
|
|
33
|
+
notebook_server_id = cls._read_variable('SINGLESTOREDB_NOTEBOOK_SERVER_ID')
|
|
34
|
+
is_local_dev_env_var = cls._read_variable('SINGLESTOREDB_IS_LOCAL_DEV')
|
|
31
35
|
|
|
32
36
|
workload_type = os.environ.get('SINGLESTOREDB_WORKLOAD_TYPE')
|
|
33
37
|
running_interactively = workload_type == 'InteractiveNotebook'
|
|
@@ -49,10 +53,12 @@ class AppConfig:
|
|
|
49
53
|
listen_port=int(port),
|
|
50
54
|
base_url=base_url,
|
|
51
55
|
base_path=base_path,
|
|
56
|
+
notebook_server_id=notebook_server_id,
|
|
52
57
|
app_token=app_token,
|
|
53
58
|
user_token=user_token,
|
|
54
59
|
running_interactively=running_interactively,
|
|
55
60
|
is_gateway_enabled=is_gateway_enabled,
|
|
61
|
+
is_local_dev=is_local_dev_env_var == 'true',
|
|
56
62
|
)
|
|
57
63
|
|
|
58
64
|
@property
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
2
4
|
from typing import Optional
|
|
3
5
|
|
|
4
6
|
|
|
@@ -8,3 +10,9 @@ class ConnectionInfo:
|
|
|
8
10
|
|
|
9
11
|
# Only present in interactive mode
|
|
10
12
|
token: Optional[str]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class UdfConnectionInfo:
|
|
17
|
+
url: str
|
|
18
|
+
functions: Dict[str, Any]
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from ..functions.ext.asgi import Application
|
|
6
|
+
from ._config import AppConfig
|
|
7
|
+
from ._connection_info import UdfConnectionInfo
|
|
8
|
+
from ._process import kill_process_by_port
|
|
9
|
+
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from ._uvicorn_util import AwaitableUvicornServer
|
|
12
|
+
|
|
13
|
+
# Keep track of currently running server
|
|
14
|
+
_running_server: 'typing.Optional[AwaitableUvicornServer]' = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def run_udf_app(
|
|
18
|
+
replace_existing: bool,
|
|
19
|
+
log_level: str = 'error',
|
|
20
|
+
kill_existing_app_server: bool = True,
|
|
21
|
+
) -> UdfConnectionInfo:
|
|
22
|
+
global _running_server
|
|
23
|
+
from ._uvicorn_util import AwaitableUvicornServer
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
import uvicorn
|
|
27
|
+
except ImportError:
|
|
28
|
+
raise ImportError('package uvicorn is required to run python udfs')
|
|
29
|
+
|
|
30
|
+
app_config = AppConfig.from_env()
|
|
31
|
+
|
|
32
|
+
if kill_existing_app_server:
|
|
33
|
+
# Shutdown the server gracefully if it was started by us.
|
|
34
|
+
# Since the uvicorn server doesn't start a new subprocess
|
|
35
|
+
# killing the process would result in kernel dying.
|
|
36
|
+
if _running_server is not None:
|
|
37
|
+
await _running_server.shutdown()
|
|
38
|
+
_running_server = None
|
|
39
|
+
|
|
40
|
+
# Kill if any other process is occupying the port
|
|
41
|
+
kill_process_by_port(app_config.listen_port)
|
|
42
|
+
|
|
43
|
+
base_url = generate_base_url(app_config)
|
|
44
|
+
|
|
45
|
+
udf_suffix = ''
|
|
46
|
+
if app_config.running_interactively:
|
|
47
|
+
udf_suffix = '_test'
|
|
48
|
+
app = Application(url=base_url, app_mode='managed', name_suffix=udf_suffix)
|
|
49
|
+
|
|
50
|
+
config = uvicorn.Config(
|
|
51
|
+
app,
|
|
52
|
+
host='0.0.0.0',
|
|
53
|
+
port=app_config.listen_port,
|
|
54
|
+
log_level=log_level,
|
|
55
|
+
)
|
|
56
|
+
_running_server = AwaitableUvicornServer(config)
|
|
57
|
+
|
|
58
|
+
# Register the functions
|
|
59
|
+
app.register_functions(replace=replace_existing)
|
|
60
|
+
|
|
61
|
+
asyncio.create_task(_running_server.serve())
|
|
62
|
+
await _running_server.wait_for_startup()
|
|
63
|
+
|
|
64
|
+
print(f'Python UDF registered at {base_url}')
|
|
65
|
+
|
|
66
|
+
return UdfConnectionInfo(base_url, app.get_function_info())
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def generate_base_url(app_config: AppConfig) -> str:
|
|
70
|
+
if not app_config.is_gateway_enabled:
|
|
71
|
+
raise RuntimeError('Python UDFs are not available if Nova Gateway is not enabled')
|
|
72
|
+
|
|
73
|
+
if not app_config.running_interactively:
|
|
74
|
+
return app_config.base_url
|
|
75
|
+
|
|
76
|
+
# generate python udf endpoint for interactive notebooks
|
|
77
|
+
gateway_url = os.environ.get('SINGLESTOREDB_NOVA_GATEWAY_ENDPOINT')
|
|
78
|
+
if app_config.is_local_dev:
|
|
79
|
+
gateway_url = os.environ.get('SINGLESTOREDB_NOVA_GATEWAY_DEV_ENDPOINT')
|
|
80
|
+
if gateway_url is None:
|
|
81
|
+
raise RuntimeError(
|
|
82
|
+
'Missing SINGLESTOREDB_NOVA_GATEWAY_DEV_ENDPOINT environment variable.',
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return f'{gateway_url}/pythonudfs/{app_config.notebook_server_id}/interactive/'
|
|
@@ -317,7 +317,7 @@ register_option(
|
|
|
317
317
|
'external_function.app_mode', 'string',
|
|
318
318
|
functools.partial(
|
|
319
319
|
check_str,
|
|
320
|
-
valid_values=['remote', 'collocated'],
|
|
320
|
+
valid_values=['remote', 'collocated', 'managed'],
|
|
321
321
|
),
|
|
322
322
|
'remote',
|
|
323
323
|
'Specifies the mode of operation of the external function application.',
|
|
@@ -407,6 +407,18 @@ register_option(
|
|
|
407
407
|
environ=['SINGLESTOREDB_EXT_FUNC_LOG_LEVEL'],
|
|
408
408
|
)
|
|
409
409
|
|
|
410
|
+
register_option(
|
|
411
|
+
'external_function.name_prefix', 'string', check_str, '',
|
|
412
|
+
'Prefix to add to external function names.',
|
|
413
|
+
environ=['SINGLESTOREDB_EXT_FUNC_NAME_PREFIX'],
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
register_option(
|
|
417
|
+
'external_function.name_suffix', 'string', check_str, '',
|
|
418
|
+
'Suffix to add to external function names.',
|
|
419
|
+
environ=['SINGLESTOREDB_EXT_FUNC_NAME_SUFFIX'],
|
|
420
|
+
)
|
|
421
|
+
|
|
410
422
|
register_option(
|
|
411
423
|
'external_function.connection', 'string', check_str,
|
|
412
424
|
os.environ.get('SINGLESTOREDB_URL') or None,
|
|
@@ -415,7 +427,7 @@ register_option(
|
|
|
415
427
|
)
|
|
416
428
|
|
|
417
429
|
register_option(
|
|
418
|
-
'external_function.host', 'string', check_str, '
|
|
430
|
+
'external_function.host', 'string', check_str, 'localhost',
|
|
419
431
|
'Specifies the host to bind the server to.',
|
|
420
432
|
environ=['SINGLESTOREDB_EXT_FUNC_HOST'],
|
|
421
433
|
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .decorator import udf # noqa: F401
|
|
2
|
+
from .typing import Masked # noqa: F401
|
|
3
|
+
from .typing import Table # noqa: F401
|
|
4
|
+
from .utils import VectorTypes
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
F32 = VectorTypes.F32
|
|
8
|
+
F64 = VectorTypes.F64
|
|
9
|
+
I8 = VectorTypes.I8
|
|
10
|
+
I16 = VectorTypes.I16
|
|
11
|
+
I32 = VectorTypes.I32
|
|
12
|
+
I64 = VectorTypes.I64
|