MindsDB 25.9.3rc1__py3-none-any.whl → 25.10.0__py3-none-any.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.
Potentially problematic release.
This version of MindsDB might be problematic. Click here for more details.
- mindsdb/__about__.py +1 -1
- mindsdb/__main__.py +1 -9
- mindsdb/api/a2a/__init__.py +1 -1
- mindsdb/api/a2a/agent.py +9 -1
- mindsdb/api/a2a/common/server/server.py +4 -0
- mindsdb/api/a2a/common/server/task_manager.py +8 -1
- mindsdb/api/a2a/common/types.py +66 -0
- mindsdb/api/a2a/task_manager.py +50 -0
- mindsdb/api/common/middleware.py +1 -1
- mindsdb/api/executor/command_executor.py +49 -36
- mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +7 -13
- mindsdb/api/executor/datahub/datanodes/integration_datanode.py +2 -2
- mindsdb/api/executor/datahub/datanodes/system_tables.py +2 -1
- mindsdb/api/executor/planner/query_prepare.py +2 -20
- mindsdb/api/executor/utilities/sql.py +5 -4
- mindsdb/api/http/initialize.py +76 -60
- mindsdb/api/http/namespaces/agents.py +0 -3
- mindsdb/api/http/namespaces/chatbots.py +0 -5
- mindsdb/api/http/namespaces/file.py +2 -0
- mindsdb/api/http/namespaces/handlers.py +10 -5
- mindsdb/api/http/namespaces/knowledge_bases.py +20 -0
- mindsdb/api/http/namespaces/sql.py +2 -2
- mindsdb/api/http/start.py +2 -2
- mindsdb/api/mysql/mysql_proxy/utilities/dump.py +8 -2
- mindsdb/integrations/handlers/byom_handler/byom_handler.py +2 -10
- mindsdb/integrations/handlers/databricks_handler/databricks_handler.py +98 -46
- mindsdb/integrations/handlers/druid_handler/druid_handler.py +32 -40
- mindsdb/integrations/handlers/gitlab_handler/gitlab_handler.py +5 -2
- mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +438 -100
- mindsdb/integrations/handlers/mssql_handler/requirements_odbc.txt +3 -0
- mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +235 -3
- mindsdb/integrations/handlers/oracle_handler/__init__.py +2 -0
- mindsdb/integrations/handlers/oracle_handler/connection_args.py +7 -1
- mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +321 -16
- mindsdb/integrations/handlers/oracle_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +2 -2
- mindsdb/integrations/handlers/shopify_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/shopify_handler/shopify_handler.py +57 -3
- mindsdb/integrations/handlers/zendesk_handler/zendesk_tables.py +144 -111
- mindsdb/integrations/libs/response.py +2 -2
- mindsdb/integrations/utilities/handlers/auth_utilities/snowflake/__init__.py +1 -0
- mindsdb/integrations/utilities/handlers/auth_utilities/snowflake/snowflake_jwt_gen.py +151 -0
- mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +24 -21
- mindsdb/interfaces/agents/agents_controller.py +0 -2
- mindsdb/interfaces/data_catalog/data_catalog_loader.py +6 -7
- mindsdb/interfaces/data_catalog/data_catalog_reader.py +15 -4
- mindsdb/interfaces/database/data_handlers_cache.py +190 -0
- mindsdb/interfaces/database/database.py +3 -3
- mindsdb/interfaces/database/integrations.py +1 -121
- mindsdb/interfaces/database/projects.py +2 -6
- mindsdb/interfaces/database/views.py +1 -4
- mindsdb/interfaces/jobs/jobs_controller.py +0 -4
- mindsdb/interfaces/jobs/scheduler.py +0 -1
- mindsdb/interfaces/knowledge_base/controller.py +197 -108
- mindsdb/interfaces/knowledge_base/evaluate.py +36 -41
- mindsdb/interfaces/knowledge_base/executor.py +11 -0
- mindsdb/interfaces/knowledge_base/llm_client.py +51 -17
- mindsdb/interfaces/model/model_controller.py +4 -4
- mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +4 -10
- mindsdb/interfaces/skills/skills_controller.py +1 -4
- mindsdb/interfaces/storage/db.py +16 -6
- mindsdb/interfaces/triggers/triggers_controller.py +1 -3
- mindsdb/utilities/config.py +19 -2
- mindsdb/utilities/exception.py +2 -2
- mindsdb/utilities/json_encoder.py +24 -10
- mindsdb/utilities/render/sqlalchemy_render.py +15 -14
- mindsdb/utilities/starters.py +0 -10
- {mindsdb-25.9.3rc1.dist-info → mindsdb-25.10.0.dist-info}/METADATA +278 -264
- {mindsdb-25.9.3rc1.dist-info → mindsdb-25.10.0.dist-info}/RECORD +72 -86
- mindsdb/api/postgres/__init__.py +0 -0
- mindsdb/api/postgres/postgres_proxy/__init__.py +0 -0
- mindsdb/api/postgres/postgres_proxy/executor/__init__.py +0 -1
- mindsdb/api/postgres/postgres_proxy/executor/executor.py +0 -182
- mindsdb/api/postgres/postgres_proxy/postgres_packets/__init__.py +0 -0
- mindsdb/api/postgres/postgres_proxy/postgres_packets/errors.py +0 -322
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_fields.py +0 -34
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message.py +0 -31
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_formats.py +0 -1265
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_identifiers.py +0 -31
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_packets.py +0 -265
- mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +0 -477
- mindsdb/api/postgres/postgres_proxy/utilities/__init__.py +0 -10
- mindsdb/api/postgres/start.py +0 -11
- mindsdb/integrations/handlers/mssql_handler/tests/__init__.py +0 -0
- mindsdb/integrations/handlers/mssql_handler/tests/test_mssql_handler.py +0 -169
- mindsdb/integrations/handlers/oracle_handler/tests/__init__.py +0 -0
- mindsdb/integrations/handlers/oracle_handler/tests/test_oracle_handler.py +0 -32
- {mindsdb-25.9.3rc1.dist-info → mindsdb-25.10.0.dist-info}/WHEEL +0 -0
- {mindsdb-25.9.3rc1.dist-info → mindsdb-25.10.0.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.9.3rc1.dist-info → mindsdb-25.10.0.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,23 @@
|
|
|
1
|
-
import copy
|
|
2
1
|
import os
|
|
3
2
|
from typing import List
|
|
4
3
|
|
|
5
4
|
from openai import OpenAI, AzureOpenAI
|
|
6
5
|
|
|
7
6
|
from mindsdb.integrations.utilities.handler_utils import get_api_key
|
|
8
|
-
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from mindsdb.integrations.handlers.openai_handler.helpers import retry_with_exponential_backoff
|
|
10
|
+
except ImportError:
|
|
11
|
+
|
|
12
|
+
def retry_with_exponential_backoff(func):
|
|
13
|
+
"""
|
|
14
|
+
An empty decorator
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def wrapper(*args, **kwargs):
|
|
18
|
+
return func(*args, **kwargs)
|
|
19
|
+
|
|
20
|
+
return wrapper
|
|
9
21
|
|
|
10
22
|
|
|
11
23
|
class LLMClient:
|
|
@@ -14,12 +26,8 @@ class LLMClient:
|
|
|
14
26
|
It chooses openai client or litellm handler depending on the config
|
|
15
27
|
"""
|
|
16
28
|
|
|
17
|
-
def __init__(self,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if llm_params:
|
|
21
|
-
params.update(llm_params)
|
|
22
|
-
|
|
29
|
+
def __init__(self, params: dict = None, session=None):
|
|
30
|
+
self._session = session
|
|
23
31
|
self.params = params
|
|
24
32
|
|
|
25
33
|
self.provider = params.get("provider", "openai")
|
|
@@ -27,11 +35,13 @@ class LLMClient:
|
|
|
27
35
|
if "api_key" not in params:
|
|
28
36
|
params["api_key"] = get_api_key(self.provider, params, strict=False)
|
|
29
37
|
|
|
38
|
+
self.engine = "openai"
|
|
39
|
+
|
|
30
40
|
if self.provider == "azure_openai":
|
|
31
41
|
azure_api_key = params.get("api_key") or os.getenv("AZURE_OPENAI_API_KEY")
|
|
32
42
|
azure_api_endpoint = params.get("base_url") or os.environ.get("AZURE_OPENAI_ENDPOINT")
|
|
33
43
|
azure_api_version = params.get("api_version") or os.environ.get("AZURE_OPENAI_API_VERSION")
|
|
34
|
-
self.
|
|
44
|
+
self.client = AzureOpenAI(
|
|
35
45
|
api_key=azure_api_key, azure_endpoint=azure_api_endpoint, api_version=azure_api_version, max_retries=2
|
|
36
46
|
)
|
|
37
47
|
elif self.provider == "openai":
|
|
@@ -41,34 +51,58 @@ class LLMClient:
|
|
|
41
51
|
if base_url:
|
|
42
52
|
kwargs["base_url"] = base_url
|
|
43
53
|
self.client = OpenAI(**kwargs)
|
|
44
|
-
|
|
54
|
+
elif self.provider == "ollama":
|
|
55
|
+
kwargs = params.copy()
|
|
56
|
+
kwargs.pop("model_name")
|
|
57
|
+
kwargs.pop("provider", None)
|
|
58
|
+
if kwargs["api_key"] is None:
|
|
59
|
+
kwargs["api_key"] = "n/a"
|
|
60
|
+
self.client = OpenAI(**kwargs)
|
|
45
61
|
else:
|
|
46
62
|
# try to use litellm
|
|
47
|
-
|
|
63
|
+
if self._session is None:
|
|
64
|
+
from mindsdb.api.executor.controllers.session_controller import SessionController
|
|
48
65
|
|
|
49
|
-
|
|
50
|
-
module =
|
|
66
|
+
self._session = SessionController()
|
|
67
|
+
module = self._session.integration_controller.get_handler_module("litellm")
|
|
51
68
|
|
|
52
69
|
if module is None or module.Handler is None:
|
|
53
70
|
raise ValueError(f'Unable to use "{self.provider}" provider. Litellm handler is not installed')
|
|
54
71
|
|
|
55
72
|
self.client = module.Handler
|
|
73
|
+
self.engine = "litellm"
|
|
74
|
+
|
|
75
|
+
@retry_with_exponential_backoff()
|
|
76
|
+
def embeddings(self, messages: List[str]):
|
|
77
|
+
params = self.params
|
|
78
|
+
if self.engine == "openai":
|
|
79
|
+
response = self.client.embeddings.create(
|
|
80
|
+
model=params["model_name"],
|
|
81
|
+
input=messages,
|
|
82
|
+
)
|
|
83
|
+
return [item.embedding for item in response.data]
|
|
84
|
+
else:
|
|
85
|
+
kwargs = params.copy()
|
|
86
|
+
model = kwargs.pop("model_name")
|
|
87
|
+
kwargs.pop("provider", None)
|
|
88
|
+
|
|
89
|
+
return self.client.embeddings(self.provider, model=model, messages=messages, args=kwargs)
|
|
56
90
|
|
|
57
|
-
def completion(self, messages: List[dict], json_output: bool = False) -> str:
|
|
91
|
+
def completion(self, messages: List[dict], json_output: bool = False) -> List[str]:
|
|
58
92
|
"""
|
|
59
93
|
Call LLM completion and get response
|
|
60
94
|
"""
|
|
61
95
|
params = self.params
|
|
62
96
|
params["json_output"] = json_output
|
|
63
|
-
if self.
|
|
97
|
+
if self.engine == "openai":
|
|
64
98
|
response = self.client.chat.completions.create(
|
|
65
99
|
model=params["model_name"],
|
|
66
100
|
messages=messages,
|
|
67
101
|
)
|
|
68
|
-
return
|
|
102
|
+
return [item.message.content for item in response.choices]
|
|
69
103
|
else:
|
|
70
104
|
kwargs = params.copy()
|
|
71
105
|
model = kwargs.pop("model_name")
|
|
72
106
|
kwargs.pop("provider", None)
|
|
73
107
|
response = self.client.completion(self.provider, model=model, messages=messages, args=kwargs)
|
|
74
|
-
return
|
|
108
|
+
return [item.message.content for item in response.choices]
|
|
@@ -246,8 +246,8 @@ class ModelController:
|
|
|
246
246
|
|
|
247
247
|
def prepare_create_statement(self, statement, database_controller):
|
|
248
248
|
# extract data from Create model or Retrain statement and prepare it for using in crate and retrain functions
|
|
249
|
-
project_name = statement.name.parts[0]
|
|
250
|
-
model_name = statement.name.parts[1]
|
|
249
|
+
project_name = statement.name.parts[0]
|
|
250
|
+
model_name = statement.name.parts[1]
|
|
251
251
|
|
|
252
252
|
sql_task = None
|
|
253
253
|
if statement.task is not None:
|
|
@@ -294,11 +294,11 @@ class ModelController:
|
|
|
294
294
|
def create_model(self, statement, ml_handler):
|
|
295
295
|
params = self.prepare_create_statement(statement, ml_handler.database_controller)
|
|
296
296
|
|
|
297
|
-
existing_projects_meta = ml_handler.database_controller.get_dict(filter_type="project")
|
|
297
|
+
existing_projects_meta = ml_handler.database_controller.get_dict(filter_type="project", lowercase=False)
|
|
298
298
|
if params["project_name"] not in existing_projects_meta:
|
|
299
299
|
raise EntityNotExistsError("Project does not exist", params["project_name"])
|
|
300
300
|
|
|
301
|
-
project = ml_handler.database_controller.get_project(name=params["project_name"])
|
|
301
|
+
project = ml_handler.database_controller.get_project(name=params["project_name"], strict_case=True)
|
|
302
302
|
project_tables = project.get_tables()
|
|
303
303
|
if params["model_name"] in project_tables:
|
|
304
304
|
raise EntityExistsError("Model already exists", f"{params['project_name']}.{params['model_name']}")
|
|
@@ -177,29 +177,23 @@ class MindsDBSQLToolkit(SQLDatabaseToolkit):
|
|
|
177
177
|
|
|
178
178
|
Query Types and Examples:
|
|
179
179
|
1. Basic semantic search:
|
|
180
|
-
kb_query_tool("SELECT * FROM kb_name WHERE
|
|
180
|
+
kb_query_tool("SELECT * FROM kb_name WHERE chunk_content = 'your search query';")
|
|
181
181
|
|
|
182
182
|
2. Metadata filtering:
|
|
183
183
|
kb_query_tool("SELECT * FROM kb_name WHERE metadata_field = 'value';")
|
|
184
184
|
|
|
185
185
|
3. Combined search:
|
|
186
|
-
kb_query_tool("SELECT * FROM kb_name WHERE
|
|
186
|
+
kb_query_tool("SELECT * FROM kb_name WHERE chunk_content = 'query' AND metadata_field = 'value';")
|
|
187
187
|
|
|
188
188
|
4. Setting relevance threshold:
|
|
189
|
-
kb_query_tool("SELECT * FROM kb_name WHERE
|
|
189
|
+
kb_query_tool("SELECT * FROM kb_name WHERE chunk_content = 'query' AND relevance_threshold = 0.7;")
|
|
190
190
|
|
|
191
191
|
5. Limiting results:
|
|
192
|
-
kb_query_tool("SELECT * FROM kb_name WHERE
|
|
192
|
+
kb_query_tool("SELECT * FROM kb_name WHERE chunk_content = 'query' LIMIT 5;")
|
|
193
193
|
|
|
194
194
|
6. Getting sample data:
|
|
195
195
|
kb_query_tool("SELECT * FROM kb_name LIMIT 3;")
|
|
196
196
|
|
|
197
|
-
7. Don't use LIKE operator on content filter ie semantic search:
|
|
198
|
-
SELECT * FROM `test_kb` WHERE content LIKE '%population of New York%' $STOP$
|
|
199
|
-
|
|
200
|
-
Like is not supported, use the following instead:
|
|
201
|
-
SELECT * FROM `test_kb` WHERE content = 'population of New York'
|
|
202
|
-
|
|
203
197
|
Result Format:
|
|
204
198
|
- Results include: id, chunk_id, chunk_content, metadata, distance, and relevance columns
|
|
205
199
|
- The metadata column contains a JSON object with all metadata fields
|
|
@@ -100,10 +100,7 @@ class SkillsController:
|
|
|
100
100
|
project_name = default_project
|
|
101
101
|
project = self.project_controller.get(name=project_name)
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
raise ValueError(f"The name must be in lower case: {name}")
|
|
105
|
-
|
|
106
|
-
skill = self.get_skill(name, project_name)
|
|
103
|
+
skill = self.get_skill(name, project_name, strict_case=True)
|
|
107
104
|
|
|
108
105
|
if skill is not None:
|
|
109
106
|
raise ValueError(f"Skill with name already exists: {name}")
|
mindsdb/interfaces/storage/db.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import orjson
|
|
2
3
|
import datetime
|
|
3
4
|
from typing import Dict, List, Optional
|
|
4
5
|
|
|
@@ -47,10 +48,20 @@ def init(connection_str: str = None):
|
|
|
47
48
|
global Base, session, engine
|
|
48
49
|
if connection_str is None:
|
|
49
50
|
connection_str = config["storage_db"]
|
|
51
|
+
# Use orjson with our CustomJSONEncoder.default for JSON serialization
|
|
52
|
+
_default_json = CustomJSONEncoder().default
|
|
53
|
+
|
|
54
|
+
def _json_serializer(value):
|
|
55
|
+
return orjson.dumps(
|
|
56
|
+
value,
|
|
57
|
+
default=_default_json,
|
|
58
|
+
option=orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_PASSTHROUGH_DATETIME,
|
|
59
|
+
).decode("utf-8")
|
|
60
|
+
|
|
50
61
|
base_args = {
|
|
51
62
|
"pool_size": 30,
|
|
52
63
|
"max_overflow": 200,
|
|
53
|
-
"json_serializer":
|
|
64
|
+
"json_serializer": _json_serializer,
|
|
54
65
|
}
|
|
55
66
|
engine = create_engine(connection_str, echo=False, **base_args)
|
|
56
67
|
session = scoped_session(sessionmaker(bind=engine, autoflush=True))
|
|
@@ -534,11 +545,10 @@ class KnowledgeBase(Base):
|
|
|
534
545
|
reranking_model = params.pop("reranking_model", None)
|
|
535
546
|
|
|
536
547
|
if not with_secrets:
|
|
537
|
-
|
|
538
|
-
embedding_model
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
reranking_model["api_key"] = "******"
|
|
548
|
+
for key in ("api_key", "private_key"):
|
|
549
|
+
for el in (embedding_model, reranking_model):
|
|
550
|
+
if el and key in el:
|
|
551
|
+
el[key] = "******"
|
|
542
552
|
|
|
543
553
|
return {
|
|
544
554
|
"id": self.id,
|
|
@@ -14,8 +14,6 @@ class TriggersController:
|
|
|
14
14
|
OBJECT_TYPE = "trigger"
|
|
15
15
|
|
|
16
16
|
def add(self, name, project_name, table, query_str, columns=None):
|
|
17
|
-
name = name.lower()
|
|
18
|
-
|
|
19
17
|
if project_name is None:
|
|
20
18
|
project_name = config.get("default_project")
|
|
21
19
|
project_controller = ProjectController()
|
|
@@ -155,7 +153,7 @@ class TriggersController:
|
|
|
155
153
|
{
|
|
156
154
|
"id": record.object_id,
|
|
157
155
|
"project": project_names[record.project_id],
|
|
158
|
-
"name": record.name
|
|
156
|
+
"name": record.name,
|
|
159
157
|
"database": database_names.get(record.database_id, "?"),
|
|
160
158
|
"table": record.table_name,
|
|
161
159
|
"query": record.query_str,
|
mindsdb/utilities/config.py
CHANGED
|
@@ -181,7 +181,6 @@ class Config:
|
|
|
181
181
|
"max_restart_count": 1,
|
|
182
182
|
"max_restart_interval_seconds": 60,
|
|
183
183
|
},
|
|
184
|
-
"postgres": {"host": api_host, "port": "55432", "database": "mindsdb"},
|
|
185
184
|
"litellm": {
|
|
186
185
|
"host": "0.0.0.0", # API server binds to all interfaces by default
|
|
187
186
|
"port": "8000",
|
|
@@ -234,7 +233,17 @@ class Config:
|
|
|
234
233
|
|
|
235
234
|
# region storage root path
|
|
236
235
|
if os.environ.get("MINDSDB_STORAGE_DIR", "") != "":
|
|
237
|
-
|
|
236
|
+
storage_root_path = Path(os.environ["MINDSDB_STORAGE_DIR"])
|
|
237
|
+
self._env_config["paths"] = {
|
|
238
|
+
"root": storage_root_path,
|
|
239
|
+
"content": storage_root_path / "content",
|
|
240
|
+
"storage": storage_root_path / "storage",
|
|
241
|
+
"static": storage_root_path / "static",
|
|
242
|
+
"tmp": storage_root_path / "tmp",
|
|
243
|
+
"log": storage_root_path / "log",
|
|
244
|
+
"cache": storage_root_path / "cache",
|
|
245
|
+
"locks": storage_root_path / "locks",
|
|
246
|
+
}
|
|
238
247
|
# endregion
|
|
239
248
|
|
|
240
249
|
# region vars: permanent storage disabled?
|
|
@@ -356,6 +365,14 @@ class Config:
|
|
|
356
365
|
self._env_config["gui"]["open_on_start"] = False
|
|
357
366
|
self._env_config["gui"]["autoupdate"] = False
|
|
358
367
|
|
|
368
|
+
mindsdb_gui_autoupdate = os.environ.get("MINDSDB_GUI_AUTOUPDATE", "").lower()
|
|
369
|
+
if mindsdb_gui_autoupdate in ("0", "false"):
|
|
370
|
+
self._env_config["gui"]["autoupdate"] = False
|
|
371
|
+
elif mindsdb_gui_autoupdate in ("1", "true"):
|
|
372
|
+
self._env_config["gui"]["autoupdate"] = True
|
|
373
|
+
elif mindsdb_gui_autoupdate != "":
|
|
374
|
+
raise ValueError(f"Wrong value of env var MINDSDB_GUI_AUTOUPDATE={mindsdb_gui_autoupdate}")
|
|
375
|
+
|
|
359
376
|
def fetch_auto_config(self) -> bool:
|
|
360
377
|
"""Load dict readed from config.auto.json to `auto_config`.
|
|
361
378
|
Do it only if `auto_config` was not loaded before or config.auto.json been changed.
|
mindsdb/utilities/exception.py
CHANGED
|
@@ -54,7 +54,7 @@ class QueryError(MindsDBError):
|
|
|
54
54
|
db_error_msg: str | None = None,
|
|
55
55
|
failed_query: str | None = None,
|
|
56
56
|
is_external: bool = True,
|
|
57
|
-
|
|
57
|
+
is_expected: bool = False,
|
|
58
58
|
) -> None:
|
|
59
59
|
self.mysql_error_code = ERR.ER_UNKNOWN_ERROR
|
|
60
60
|
self.db_name = db_name
|
|
@@ -62,7 +62,7 @@ class QueryError(MindsDBError):
|
|
|
62
62
|
self.db_error_msg = db_error_msg
|
|
63
63
|
self.failed_query = failed_query
|
|
64
64
|
self.is_external = is_external
|
|
65
|
-
self.
|
|
65
|
+
self.is_expected = is_expected
|
|
66
66
|
|
|
67
67
|
def __str__(self) -> str:
|
|
68
68
|
return format_db_error_message(
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
from datetime import datetime, date, timedelta
|
|
2
2
|
from decimal import Decimal
|
|
3
|
-
import numpy as np
|
|
4
3
|
import pandas as pd
|
|
5
|
-
import
|
|
6
|
-
|
|
4
|
+
import numpy as np
|
|
5
|
+
import orjson
|
|
7
6
|
from flask.json.provider import DefaultJSONProvider
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
class CustomJSONEncoder
|
|
9
|
+
class CustomJSONEncoder:
|
|
11
10
|
def default(self, obj):
|
|
12
11
|
if isinstance(obj, timedelta):
|
|
13
12
|
return str(obj)
|
|
@@ -15,12 +14,10 @@ class CustomJSONEncoder(json.JSONEncoder):
|
|
|
15
14
|
return obj.strftime("%Y-%m-%d %H:%M:%S.%f")
|
|
16
15
|
if isinstance(obj, date):
|
|
17
16
|
return obj.strftime("%Y-%m-%d")
|
|
17
|
+
if isinstance(obj, Decimal):
|
|
18
|
+
return float(obj)
|
|
18
19
|
if isinstance(obj, np.bool_):
|
|
19
20
|
return bool(obj)
|
|
20
|
-
if isinstance(obj, np.int8) or isinstance(obj, np.int16) or isinstance(obj, np.int32) or isinstance(obj, np.int64):
|
|
21
|
-
return int(obj)
|
|
22
|
-
if isinstance(obj, np.float16) or isinstance(obj, np.float32) or isinstance(obj, np.float64) or isinstance(obj, Decimal):
|
|
23
|
-
return float(obj)
|
|
24
21
|
if isinstance(obj, np.ndarray):
|
|
25
22
|
return obj.tolist()
|
|
26
23
|
if pd.isnull(obj):
|
|
@@ -29,5 +26,22 @@ class CustomJSONEncoder(json.JSONEncoder):
|
|
|
29
26
|
return str(obj)
|
|
30
27
|
|
|
31
28
|
|
|
32
|
-
class
|
|
33
|
-
|
|
29
|
+
class ORJSONProvider(DefaultJSONProvider):
|
|
30
|
+
"""
|
|
31
|
+
Use orjson to serialize data instead of flask json provider.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def dumps(self, obj, **kwargs):
|
|
35
|
+
return orjson.dumps(
|
|
36
|
+
obj,
|
|
37
|
+
option=(
|
|
38
|
+
orjson.OPT_SERIALIZE_NUMPY
|
|
39
|
+
| orjson.OPT_NON_STR_KEYS
|
|
40
|
+
# keep this for using CustomJSON encoder
|
|
41
|
+
| orjson.OPT_PASSTHROUGH_DATETIME
|
|
42
|
+
),
|
|
43
|
+
default=CustomJSONEncoder().default,
|
|
44
|
+
).decode("utf-8")
|
|
45
|
+
|
|
46
|
+
def loads(self, s, **kwargs):
|
|
47
|
+
return orjson.loads(s)
|
|
@@ -10,6 +10,7 @@ from sqlalchemy.dialects import mysql, postgresql, sqlite, mssql, oracle
|
|
|
10
10
|
from sqlalchemy.schema import CreateTable, DropTable
|
|
11
11
|
from sqlalchemy.sql import ColumnElement
|
|
12
12
|
from sqlalchemy.sql import functions as sa_fnc
|
|
13
|
+
from sqlalchemy.engine.interfaces import Dialect
|
|
13
14
|
|
|
14
15
|
from mindsdb_sql_parser import ast
|
|
15
16
|
|
|
@@ -80,27 +81,27 @@ def get_is_quoted(identifier: ast.Identifier):
|
|
|
80
81
|
return quoted
|
|
81
82
|
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"Snowflake": oracle,
|
|
93
|
-
}
|
|
84
|
+
dialects = {
|
|
85
|
+
"mysql": mysql,
|
|
86
|
+
"postgresql": postgresql,
|
|
87
|
+
"postgres": postgresql,
|
|
88
|
+
"sqlite": sqlite,
|
|
89
|
+
"mssql": mssql,
|
|
90
|
+
"oracle": oracle,
|
|
91
|
+
}
|
|
92
|
+
|
|
94
93
|
|
|
94
|
+
class SqlalchemyRender:
|
|
95
|
+
def __init__(self, dialect_name: str | Dialect):
|
|
95
96
|
if isinstance(dialect_name, str):
|
|
96
97
|
dialect = dialects[dialect_name].dialect
|
|
97
98
|
else:
|
|
98
99
|
dialect = dialect_name
|
|
99
100
|
|
|
100
101
|
# override dialect's preparer
|
|
101
|
-
if hasattr(dialect, "preparer"):
|
|
102
|
+
if hasattr(dialect, "preparer") and dialect.preparer.__name__ != "MDBPreparer":
|
|
102
103
|
|
|
103
|
-
class
|
|
104
|
+
class MDBPreparer(dialect.preparer):
|
|
104
105
|
def _requires_quotes(self, value: str) -> bool:
|
|
105
106
|
# check force-quote flag
|
|
106
107
|
if isinstance(value, AttributedStr):
|
|
@@ -116,7 +117,7 @@ class SqlalchemyRender:
|
|
|
116
117
|
# or (lc_value != value)
|
|
117
118
|
)
|
|
118
119
|
|
|
119
|
-
dialect.preparer =
|
|
120
|
+
dialect.preparer = MDBPreparer
|
|
120
121
|
|
|
121
122
|
# remove double percent signs
|
|
122
123
|
# https://docs.sqlalchemy.org/en/14/faq/sqlexpressions.html#why-are-percent-signs-being-doubled-up-when-stringifying-sql-statements
|
mindsdb/utilities/starters.py
CHANGED
|
@@ -18,16 +18,6 @@ def start_mysql(*args, **kwargs):
|
|
|
18
18
|
start(*args, **kwargs)
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def start_postgres(*args, **kwargs):
|
|
22
|
-
from mindsdb.utilities.log import initialize_logging
|
|
23
|
-
|
|
24
|
-
initialize_logging("postgres")
|
|
25
|
-
|
|
26
|
-
from mindsdb.api.postgres.start import start
|
|
27
|
-
|
|
28
|
-
start(*args, **kwargs)
|
|
29
|
-
|
|
30
|
-
|
|
31
21
|
def start_tasks(*args, **kwargs):
|
|
32
22
|
from mindsdb.utilities.log import initialize_logging
|
|
33
23
|
|