singlestoredb 1.12.1__tar.gz → 1.12.3__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.

Files changed (155) hide show
  1. {singlestoredb-1.12.1/singlestoredb.egg-info → singlestoredb-1.12.3}/PKG-INFO +1 -1
  2. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/accel.c +0 -7
  3. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/setup.cfg +1 -1
  4. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/__init__.py +1 -1
  5. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/config.py +1 -1
  6. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/functions/ext/asgi.py +20 -3
  7. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/functions/signature.py +8 -1
  8. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/fusion/handlers/utils.py +27 -19
  9. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/http/connection.py +9 -0
  10. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/management/files.py +1 -0
  11. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/cursors.py +8 -0
  12. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_basics.py +69 -0
  13. singlestoredb-1.12.3/singlestoredb/tests/test_fusion.py +1524 -0
  14. {singlestoredb-1.12.1 → singlestoredb-1.12.3/singlestoredb.egg-info}/PKG-INFO +1 -1
  15. singlestoredb-1.12.1/singlestoredb/tests/test_fusion.py +0 -716
  16. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/LICENSE +0 -0
  17. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/README.md +0 -0
  18. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/setup.py +0 -0
  19. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/ai/__init__.py +0 -0
  20. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/ai/embeddings.py +0 -0
  21. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/alchemy/__init__.py +0 -0
  22. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/apps/__init__.py +0 -0
  23. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/apps/_cloud_functions.py +0 -0
  24. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/apps/_config.py +0 -0
  25. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/apps/_connection_info.py +0 -0
  26. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/apps/_dashboards.py +0 -0
  27. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/apps/_process.py +0 -0
  28. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/apps/_stdout_supress.py +0 -0
  29. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/apps/_uvicorn_util.py +0 -0
  30. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/auth.py +0 -0
  31. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/connection.py +0 -0
  32. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/converters.py +0 -0
  33. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/exceptions.py +0 -0
  34. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/functions/__init__.py +0 -0
  35. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/functions/decorator.py +0 -0
  36. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/functions/dtypes.py +0 -0
  37. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/functions/ext/__init__.py +0 -0
  38. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/functions/ext/arrow.py +0 -0
  39. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/functions/ext/json.py +0 -0
  40. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/functions/ext/mmap.py +0 -0
  41. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/functions/ext/rowdat_1.py +0 -0
  42. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/functions/ext/utils.py +0 -0
  43. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/fusion/__init__.py +0 -0
  44. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/fusion/graphql.py +0 -0
  45. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/fusion/handler.py +0 -0
  46. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/fusion/handlers/__init__.py +0 -0
  47. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/fusion/handlers/export.py +0 -0
  48. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/fusion/handlers/files.py +0 -0
  49. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/fusion/handlers/job.py +0 -0
  50. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/fusion/handlers/models.py +0 -0
  51. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/fusion/handlers/stage.py +0 -0
  52. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/fusion/handlers/workspace.py +0 -0
  53. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/fusion/registry.py +0 -0
  54. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/fusion/result.py +0 -0
  55. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/http/__init__.py +0 -0
  56. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/magics/__init__.py +0 -0
  57. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/magics/run_personal.py +0 -0
  58. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/magics/run_shared.py +0 -0
  59. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/management/__init__.py +0 -0
  60. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/management/billing_usage.py +0 -0
  61. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/management/cluster.py +0 -0
  62. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/management/export.py +0 -0
  63. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/management/job.py +0 -0
  64. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/management/manager.py +0 -0
  65. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/management/organization.py +0 -0
  66. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/management/region.py +0 -0
  67. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/management/utils.py +0 -0
  68. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/management/workspace.py +0 -0
  69. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/__init__.py +0 -0
  70. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/_auth.py +0 -0
  71. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/charset.py +0 -0
  72. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/connection.py +0 -0
  73. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/constants/CLIENT.py +0 -0
  74. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/constants/COMMAND.py +0 -0
  75. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/constants/CR.py +0 -0
  76. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/constants/ER.py +0 -0
  77. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/constants/EXTENDED_TYPE.py +0 -0
  78. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/constants/FIELD_TYPE.py +0 -0
  79. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/constants/FLAG.py +0 -0
  80. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/constants/SERVER_STATUS.py +0 -0
  81. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/constants/VECTOR_TYPE.py +0 -0
  82. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/constants/__init__.py +0 -0
  83. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/converters.py +0 -0
  84. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/err.py +0 -0
  85. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/optionfile.py +0 -0
  86. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/protocol.py +0 -0
  87. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/__init__.py +0 -0
  88. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/base.py +0 -0
  89. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/conftest.py +0 -0
  90. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/test_DictCursor.py +0 -0
  91. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/test_SSCursor.py +0 -0
  92. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/test_basic.py +0 -0
  93. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/test_connection.py +0 -0
  94. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/test_converters.py +0 -0
  95. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/test_cursor.py +0 -0
  96. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/test_err.py +0 -0
  97. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/test_issues.py +0 -0
  98. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/test_load_local.py +0 -0
  99. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/test_nextset.py +0 -0
  100. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/test_optionfile.py +0 -0
  101. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/thirdparty/__init__.py +0 -0
  102. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +0 -0
  103. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +0 -0
  104. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +0 -0
  105. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +0 -0
  106. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +0 -0
  107. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +0 -0
  108. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/mysql/times.py +0 -0
  109. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/notebook/__init__.py +0 -0
  110. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/notebook/_objects.py +0 -0
  111. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/notebook/_portal.py +0 -0
  112. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/py.typed +0 -0
  113. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/pytest.py +0 -0
  114. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/server/__init__.py +0 -0
  115. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/server/docker.py +0 -0
  116. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/server/free_tier.py +0 -0
  117. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/__init__.py +0 -0
  118. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/empty.sql +0 -0
  119. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/ext_funcs/__init__.py +0 -0
  120. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/local_infile.csv +0 -0
  121. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test.ipynb +0 -0
  122. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test.sql +0 -0
  123. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test2.ipynb +0 -0
  124. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test2.sql +0 -0
  125. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_config.py +0 -0
  126. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_connection.py +0 -0
  127. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_dbapi.py +0 -0
  128. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_exceptions.py +0 -0
  129. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_ext_func.py +0 -0
  130. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_ext_func_data.py +0 -0
  131. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_http.py +0 -0
  132. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_management.py +0 -0
  133. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_plugin.py +0 -0
  134. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_results.py +0 -0
  135. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_types.py +0 -0
  136. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_udf.py +0 -0
  137. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/test_xdict.py +0 -0
  138. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/tests/utils.py +0 -0
  139. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/types.py +0 -0
  140. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/utils/__init__.py +0 -0
  141. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/utils/config.py +0 -0
  142. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/utils/convert_rows.py +0 -0
  143. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/utils/debug.py +0 -0
  144. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/utils/dtypes.py +0 -0
  145. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/utils/events.py +0 -0
  146. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/utils/mogrify.py +0 -0
  147. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/utils/results.py +0 -0
  148. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb/utils/xdict.py +0 -0
  149. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb.egg-info/SOURCES.txt +0 -0
  150. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb.egg-info/dependency_links.txt +0 -0
  151. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb.egg-info/entry_points.txt +0 -0
  152. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb.egg-info/requires.txt +0 -0
  153. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/singlestoredb.egg-info/top_level.txt +0 -0
  154. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/sqlx/__init__.py +0 -0
  155. {singlestoredb-1.12.1 → singlestoredb-1.12.3}/sqlx/magic.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: singlestoredb
3
- Version: 1.12.1
3
+ Version: 1.12.3
4
4
  Summary: Interface to the SingleStoreDB database and workspace management APIs
5
5
  Home-page: https://github.com/singlestore-labs/singlestoredb-python
6
6
  Author: SingleStore
@@ -2179,9 +2179,6 @@ static PyObject *load_rowdat_1_numpy(PyObject *self, PyObject *args, PyObject *k
2179
2179
 
2180
2180
  // Get number of columns
2181
2181
  n_cols = PyObject_Length(py_colspec);
2182
- if (n_cols == 0) {
2183
- goto error;
2184
- }
2185
2182
 
2186
2183
  // Determine column types
2187
2184
  ctypes = calloc(sizeof(int), n_cols);
@@ -3979,10 +3976,6 @@ static PyObject *load_rowdat_1(PyObject *self, PyObject *args, PyObject *kwargs)
3979
3976
  end = data + (unsigned long long)length;
3980
3977
 
3981
3978
  colspec_l = PyObject_Length(py_colspec);
3982
- if (colspec_l == 0) {
3983
- goto error;
3984
- }
3985
-
3986
3979
  ctypes = malloc(sizeof(int) * colspec_l);
3987
3980
 
3988
3981
  for (i = 0; i < colspec_l; i++) {
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = singlestoredb
3
- version = 1.12.1
3
+ version = 1.12.3
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
@@ -13,7 +13,7 @@ Examples
13
13
 
14
14
  """
15
15
 
16
- __version__ = '1.12.1'
16
+ __version__ = '1.12.3'
17
17
 
18
18
  from typing import Any
19
19
 
@@ -80,7 +80,7 @@ register_option(
80
80
  )
81
81
 
82
82
  register_option(
83
- 'charset', 'string', check_str, 'utf8',
83
+ 'charset', 'string', check_str, 'utf8mb4',
84
84
  'Specifies the character set for the session.',
85
85
  environ='SINGLESTOREDB_CHARSET',
86
86
  )
@@ -69,6 +69,12 @@ try:
69
69
  except ImportError:
70
70
  has_cloudpickle = False
71
71
 
72
+ try:
73
+ from pydantic import BaseModel
74
+ has_pydantic = True
75
+ except ImportError:
76
+ has_pydantic = False
77
+
72
78
 
73
79
  logger = utils.get_logger('singlestoredb.functions.ext.asgi')
74
80
 
@@ -138,13 +144,24 @@ def get_func_names(funcs: str) -> List[Tuple[str, str]]:
138
144
 
139
145
 
140
146
  def as_tuple(x: Any) -> Any:
141
- if hasattr(x, 'model_fields'):
142
- return tuple(x.model_fields.values())
147
+ """Convert object to tuple."""
148
+ if has_pydantic and isinstance(x, BaseModel):
149
+ return tuple(x.model_dump().values())
143
150
  if dataclasses.is_dataclass(x):
144
151
  return dataclasses.astuple(x)
145
152
  return x
146
153
 
147
154
 
155
+ def as_list_of_tuples(x: Any) -> Any:
156
+ """Convert object to a list of tuples."""
157
+ if isinstance(x, (list, tuple)) and len(x) > 0:
158
+ if has_pydantic and isinstance(x[0], BaseModel):
159
+ return [tuple(y.model_dump().values()) for y in x]
160
+ if dataclasses.is_dataclass(x[0]):
161
+ return [dataclasses.astuple(y) for y in x]
162
+ return x
163
+
164
+
148
165
  def make_func(
149
166
  name: str,
150
167
  func: Callable[..., Any],
@@ -183,7 +200,7 @@ def make_func(
183
200
  out_ids: List[int] = []
184
201
  out = []
185
202
  for i, res in zip(row_ids, func_map(func, rows)):
186
- out.extend(as_tuple(res))
203
+ out.extend(as_list_of_tuples(res))
187
204
  out_ids.extend([row_ids[i]] * (len(out)-len(out_ids)))
188
205
  return out_ids, out
189
206
 
@@ -6,6 +6,8 @@ import numbers
6
6
  import os
7
7
  import re
8
8
  import string
9
+ import sys
10
+ import types
9
11
  import typing
10
12
  from typing import Any
11
13
  from typing import Callable
@@ -32,6 +34,11 @@ except ImportError:
32
34
  from . import dtypes as dt
33
35
  from ..mysql.converters import escape_item # type: ignore
34
36
 
37
+ if sys.version_info >= (3, 10):
38
+ _UNION_TYPES = {typing.Union, types.UnionType}
39
+ else:
40
+ _UNION_TYPES = {typing.Union}
41
+
35
42
 
36
43
  array_types: Tuple[Any, ...]
37
44
 
@@ -211,7 +218,7 @@ def simplify_dtype(dtype: Any) -> List[Any]:
211
218
  args = []
212
219
 
213
220
  # Flatten Unions
214
- if origin is Union:
221
+ if origin in _UNION_TYPES:
215
222
  for x in typing.get_args(dtype):
216
223
  args.extend(simplify_dtype(x))
217
224
 
@@ -199,59 +199,65 @@ def get_deployment(
199
199
  """
200
200
  manager = get_workspace_manager()
201
201
 
202
+ #
203
+ # Search for deployment by name
204
+ #
202
205
  deployment_name = params.get('deployment_name') or \
203
206
  (params.get('in_deployment') or {}).get('deployment_name') or \
204
207
  (params.get('group') or {}).get('deployment_name') or \
205
208
  ((params.get('in') or {}).get('in_group') or {}).get('deployment_name') or \
206
209
  ((params.get('in') or {}).get('in_deployment') or {}).get('deployment_name')
210
+
207
211
  if deployment_name:
212
+ # Standard workspace group
208
213
  workspace_groups = [
209
214
  x for x in manager.workspace_groups
210
215
  if x.name == deployment_name
211
216
  ]
212
217
 
213
- starter_workspaces = []
214
- if not workspace_groups:
215
- filtered_starter_workspaces = [
216
- x for x in manager.starter_workspaces
217
- if x.name == deployment_name
218
- ]
219
-
220
- if not filtered_starter_workspaces:
221
- raise KeyError(
222
- f'no deployment found with name: {deployment_name}',
223
- )
224
-
225
- starter_workspaces = filtered_starter_workspaces
218
+ if len(workspace_groups) == 1:
219
+ return workspace_groups[0]
226
220
 
227
- if len(workspace_groups) > 1:
221
+ elif len(workspace_groups) > 1:
228
222
  ids = ', '.join(x.id for x in workspace_groups)
229
223
  raise ValueError(
230
224
  f'more than one workspace group with given name was found: {ids}',
231
225
  )
232
226
 
233
- if len(starter_workspaces) > 1:
227
+ # Starter workspace
228
+ starter_workspaces = [
229
+ x for x in manager.starter_workspaces
230
+ if x.name == deployment_name
231
+ ]
232
+
233
+ if len(starter_workspaces) == 1:
234
+ return starter_workspaces[0]
235
+
236
+ elif len(starter_workspaces) > 1:
234
237
  ids = ', '.join(x.id for x in starter_workspaces)
235
238
  raise ValueError(
236
239
  f'more than one starter workspace with given name was found: {ids}',
237
240
  )
238
241
 
239
- if workspace_groups:
240
- return workspace_groups[0]
241
- else:
242
- return starter_workspaces[0]
242
+ raise KeyError(f'no deployment found with name: {deployment_name}')
243
243
 
244
+ #
245
+ # Search for deployment by ID
246
+ #
244
247
  deployment_id = params.get('deployment_id') or \
245
248
  (params.get('in_deployment') or {}).get('deployment_id') or \
246
249
  (params.get('group') or {}).get('deployment_id') or \
247
250
  ((params.get('in') or {}).get('in_group') or {}).get('deployment_id') or \
248
251
  ((params.get('in') or {}).get('in_deployment') or {}).get('deployment_id')
252
+
249
253
  if deployment_id:
250
254
  try:
255
+ # Standard workspace group
251
256
  return manager.get_workspace_group(deployment_id)
252
257
  except ManagementError as exc:
253
258
  if exc.errno == 404:
254
259
  try:
260
+ # Starter workspace
255
261
  return manager.get_starter_workspace(deployment_id)
256
262
  except ManagementError as exc:
257
263
  if exc.errno == 404:
@@ -260,6 +266,7 @@ def get_deployment(
260
266
  else:
261
267
  raise
262
268
 
269
+ # Use workspace group from environment
263
270
  if os.environ.get('SINGLESTOREDB_WORKSPACE_GROUP'):
264
271
  try:
265
272
  return manager.get_workspace_group(
@@ -273,6 +280,7 @@ def get_deployment(
273
280
  )
274
281
  raise
275
282
 
283
+ # Use cluster from environment
276
284
  if os.environ.get('SINGLESTOREDB_CLUSTER'):
277
285
  try:
278
286
  return manager.get_starter_workspace(
@@ -43,6 +43,12 @@ try:
43
43
  except ImportError:
44
44
  has_shapely = False
45
45
 
46
+ try:
47
+ import pydantic
48
+ has_pydantic = True
49
+ except ImportError:
50
+ has_pydantic = False
51
+
46
52
  from .. import connection
47
53
  from .. import fusion
48
54
  from .. import types
@@ -533,6 +539,9 @@ class Cursor(connection.Cursor):
533
539
  self._expect_results = True
534
540
  sql_type = 'query'
535
541
 
542
+ if has_pydantic and isinstance(params, pydantic.BaseModel):
543
+ params = params.model_dump()
544
+
536
545
  self._validate_param_subs(oper, params)
537
546
 
538
547
  handler = fusion.get_handler(oper)
@@ -348,6 +348,7 @@ class FilesObjectBytesReader(io.BytesIO):
348
348
 
349
349
 
350
350
  class FileLocation(ABC):
351
+
351
352
  @abstractmethod
352
353
  def open(
353
354
  self,
@@ -8,6 +8,12 @@ from ..utils import results
8
8
  from ..utils.debug import log_query
9
9
  from ..utils.results import get_schema
10
10
 
11
+ try:
12
+ from pydantic import BaseModel
13
+ has_pydantic = True
14
+ except ImportError:
15
+ has_pydantic = False
16
+
11
17
 
12
18
  #: Regular expression for :meth:`Cursor.executemany`.
13
19
  #: executemany only supports simple bulk insert.
@@ -149,6 +155,8 @@ class Cursor(BaseCursor):
149
155
  return tuple(literal(arg) for arg in args)
150
156
  elif dtype is dict or isinstance(args, dict):
151
157
  return {key: literal(val) for (key, val) in args.items()}
158
+ elif has_pydantic and isinstance(args, BaseModel):
159
+ return {key: literal(val) for (key, val) in args.model_dump().items()}
152
160
  # If it's not a dictionary let's try escaping it anyways.
153
161
  # Worst case it will throw a Value error
154
162
  return conn.escape(args)
@@ -6,6 +6,7 @@ import decimal
6
6
  import math
7
7
  import os
8
8
  import unittest
9
+ from typing import Optional
9
10
 
10
11
  from requests.exceptions import InvalidJSONError
11
12
 
@@ -28,6 +29,12 @@ try:
28
29
  except ImportError:
29
30
  has_pygeos = False
30
31
 
32
+ try:
33
+ import pydantic
34
+ has_pydantic = True
35
+ except ImportError:
36
+ has_pydantic = False
37
+
31
38
  import singlestoredb as s2
32
39
  from . import utils
33
40
  # import traceback
@@ -1255,6 +1262,68 @@ class TestBasics(unittest.TestCase):
1255
1262
  except Exception:
1256
1263
  pass
1257
1264
 
1265
+ def test_pydantic(self):
1266
+ if not has_pydantic:
1267
+ self.skipTest('Test requires pydantic')
1268
+
1269
+ tblname = 'foo_' + str(id(self))
1270
+
1271
+ class FooData(pydantic.BaseModel):
1272
+ x: Optional[int]
1273
+ y: Optional[float]
1274
+ z: Optional[str] = None
1275
+
1276
+ self.cur.execute(f'''
1277
+ CREATE TABLE {tblname}(
1278
+ x INT,
1279
+ y DOUBLE,
1280
+ z TEXT
1281
+ )
1282
+ ''')
1283
+
1284
+ self.cur.execute(
1285
+ f'INSERT INTO {tblname}(x, y) VALUES (%(x)s, %(y)s)',
1286
+ FooData(x=2, y=3.23),
1287
+ )
1288
+
1289
+ self.cur.execute('SELECT * FROM ' + tblname)
1290
+
1291
+ assert list(sorted(self.cur.fetchall())) == \
1292
+ list(sorted([(2, 3.23, None)]))
1293
+
1294
+ self.cur.executemany(
1295
+ f'INSERT INTO {tblname}(x) VALUES (%(x)s)',
1296
+ [FooData(x=3, y=3.12), FooData(x=10, y=100.11)],
1297
+ )
1298
+
1299
+ self.cur.execute('SELECT * FROM ' + tblname)
1300
+
1301
+ assert list(sorted(self.cur.fetchall())) == \
1302
+ list(
1303
+ sorted([
1304
+ (2, 3.23, None),
1305
+ (3, None, None),
1306
+ (10, None, None),
1307
+ ]),
1308
+ )
1309
+
1310
+ def test_charset(self):
1311
+ with s2.connect(database=type(self).dbname) as conn:
1312
+ with conn.cursor() as cur:
1313
+ cur.execute('''
1314
+ select json_extract_string('{"foo":"😀"}', "bar");
1315
+ ''')
1316
+
1317
+ if 'http' in self.conn.driver:
1318
+ self.skipTest('Charset is not use in HTTP interface')
1319
+
1320
+ with self.assertRaises(s2.OperationalError):
1321
+ with s2.connect(database=type(self).dbname, charset='utf8') as conn:
1322
+ with conn.cursor() as cur:
1323
+ cur.execute('''
1324
+ select json_extract_string('{"foo":"😀"}', "bar");
1325
+ ''')
1326
+
1258
1327
 
1259
1328
  if __name__ == '__main__':
1260
1329
  import nose2