MindsDB 25.9.1.2__py3-none-any.whl → 25.9.3rc1__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 +39 -20
- mindsdb/api/a2a/agent.py +7 -9
- mindsdb/api/a2a/common/server/server.py +3 -3
- mindsdb/api/a2a/common/server/task_manager.py +4 -4
- mindsdb/api/a2a/task_manager.py +15 -17
- mindsdb/api/common/middleware.py +9 -11
- mindsdb/api/executor/command_executor.py +2 -4
- mindsdb/api/executor/datahub/datanodes/datanode.py +2 -2
- mindsdb/api/executor/datahub/datanodes/integration_datanode.py +100 -48
- mindsdb/api/executor/datahub/datanodes/project_datanode.py +8 -4
- mindsdb/api/executor/datahub/datanodes/system_tables.py +1 -1
- mindsdb/api/executor/exceptions.py +29 -10
- mindsdb/api/executor/planner/plan_join.py +17 -3
- mindsdb/api/executor/sql_query/sql_query.py +74 -74
- mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +1 -2
- mindsdb/api/executor/sql_query/steps/subselect_step.py +0 -1
- mindsdb/api/executor/utilities/functions.py +6 -6
- mindsdb/api/executor/utilities/sql.py +32 -16
- mindsdb/api/http/gui.py +5 -11
- mindsdb/api/http/initialize.py +8 -10
- mindsdb/api/http/namespaces/agents.py +10 -12
- mindsdb/api/http/namespaces/analysis.py +13 -20
- mindsdb/api/http/namespaces/auth.py +1 -1
- mindsdb/api/http/namespaces/config.py +15 -11
- mindsdb/api/http/namespaces/databases.py +140 -201
- mindsdb/api/http/namespaces/file.py +15 -4
- mindsdb/api/http/namespaces/handlers.py +7 -2
- mindsdb/api/http/namespaces/knowledge_bases.py +8 -7
- mindsdb/api/http/namespaces/models.py +94 -126
- mindsdb/api/http/namespaces/projects.py +13 -22
- mindsdb/api/http/namespaces/sql.py +33 -25
- mindsdb/api/http/namespaces/tab.py +27 -37
- mindsdb/api/http/namespaces/views.py +1 -1
- mindsdb/api/http/start.py +14 -8
- mindsdb/api/mcp/__init__.py +2 -1
- mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +15 -20
- mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +26 -50
- mindsdb/api/mysql/mysql_proxy/utilities/__init__.py +0 -1
- mindsdb/api/postgres/postgres_proxy/executor/executor.py +6 -13
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_packets.py +40 -28
- mindsdb/integrations/handlers/byom_handler/byom_handler.py +168 -185
- mindsdb/integrations/handlers/chromadb_handler/chromadb_handler.py +11 -5
- mindsdb/integrations/handlers/file_handler/file_handler.py +7 -0
- mindsdb/integrations/handlers/lightwood_handler/functions.py +45 -79
- mindsdb/integrations/handlers/openai_handler/openai_handler.py +1 -1
- mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +20 -2
- mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +18 -3
- mindsdb/integrations/handlers/shopify_handler/shopify_handler.py +25 -12
- mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +2 -1
- mindsdb/integrations/handlers/statsforecast_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/statsforecast_handler/requirements_extra.txt +1 -0
- mindsdb/integrations/handlers/web_handler/urlcrawl_helpers.py +4 -4
- mindsdb/integrations/libs/api_handler.py +10 -10
- mindsdb/integrations/libs/base.py +4 -4
- mindsdb/integrations/libs/llm/utils.py +2 -2
- mindsdb/integrations/libs/ml_handler_process/create_engine_process.py +4 -7
- mindsdb/integrations/libs/ml_handler_process/func_call_process.py +2 -7
- mindsdb/integrations/libs/ml_handler_process/learn_process.py +37 -47
- mindsdb/integrations/libs/ml_handler_process/update_engine_process.py +4 -7
- mindsdb/integrations/libs/ml_handler_process/update_process.py +2 -7
- mindsdb/integrations/libs/process_cache.py +132 -140
- mindsdb/integrations/libs/response.py +18 -12
- mindsdb/integrations/libs/vectordatabase_handler.py +26 -0
- mindsdb/integrations/utilities/files/file_reader.py +6 -7
- mindsdb/integrations/utilities/rag/config_loader.py +37 -26
- mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +59 -9
- mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +4 -4
- mindsdb/integrations/utilities/rag/retrievers/sql_retriever.py +55 -133
- mindsdb/integrations/utilities/rag/settings.py +58 -133
- mindsdb/integrations/utilities/rag/splitters/file_splitter.py +5 -15
- mindsdb/interfaces/agents/agents_controller.py +2 -1
- mindsdb/interfaces/agents/constants.py +0 -2
- mindsdb/interfaces/agents/litellm_server.py +34 -58
- mindsdb/interfaces/agents/mcp_client_agent.py +10 -10
- mindsdb/interfaces/agents/mindsdb_database_agent.py +5 -5
- mindsdb/interfaces/agents/run_mcp_agent.py +12 -21
- mindsdb/interfaces/chatbot/chatbot_task.py +20 -23
- mindsdb/interfaces/chatbot/polling.py +30 -18
- mindsdb/interfaces/data_catalog/data_catalog_loader.py +10 -10
- mindsdb/interfaces/database/integrations.py +19 -2
- mindsdb/interfaces/file/file_controller.py +6 -6
- mindsdb/interfaces/functions/controller.py +1 -1
- mindsdb/interfaces/functions/to_markdown.py +2 -2
- mindsdb/interfaces/jobs/jobs_controller.py +5 -5
- mindsdb/interfaces/jobs/scheduler.py +3 -8
- mindsdb/interfaces/knowledge_base/controller.py +54 -25
- mindsdb/interfaces/knowledge_base/preprocessing/json_chunker.py +40 -61
- mindsdb/interfaces/model/model_controller.py +170 -166
- mindsdb/interfaces/query_context/context_controller.py +14 -2
- mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +6 -4
- mindsdb/interfaces/skills/retrieval_tool.py +43 -50
- mindsdb/interfaces/skills/skill_tool.py +2 -2
- mindsdb/interfaces/skills/sql_agent.py +25 -19
- mindsdb/interfaces/storage/fs.py +114 -169
- mindsdb/interfaces/storage/json.py +19 -18
- mindsdb/interfaces/storage/model_fs.py +54 -92
- mindsdb/interfaces/tabs/tabs_controller.py +49 -72
- mindsdb/interfaces/tasks/task_monitor.py +3 -9
- mindsdb/interfaces/tasks/task_thread.py +7 -9
- mindsdb/interfaces/triggers/trigger_task.py +7 -13
- mindsdb/interfaces/triggers/triggers_controller.py +47 -50
- mindsdb/migrations/migrate.py +16 -16
- mindsdb/utilities/api_status.py +58 -0
- mindsdb/utilities/config.py +49 -0
- mindsdb/utilities/exception.py +40 -1
- mindsdb/utilities/fs.py +0 -1
- mindsdb/utilities/hooks/profiling.py +17 -14
- mindsdb/utilities/langfuse.py +40 -45
- mindsdb/utilities/log.py +272 -0
- mindsdb/utilities/ml_task_queue/consumer.py +52 -58
- mindsdb/utilities/ml_task_queue/producer.py +26 -30
- mindsdb/utilities/render/sqlalchemy_render.py +8 -7
- mindsdb/utilities/utils.py +2 -2
- {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/METADATA +266 -261
- {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/RECORD +119 -119
- mindsdb/api/mysql/mysql_proxy/utilities/exceptions.py +0 -14
- {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/WHEEL +0 -0
- {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/top_level.txt +0 -0
|
@@ -1,39 +1,58 @@
|
|
|
1
|
+
from mindsdb.api.mysql.mysql_proxy.libs.constants.mysql import ERR
|
|
1
2
|
|
|
2
3
|
|
|
3
4
|
# base exception for unknown error
|
|
4
5
|
class UnknownError(Exception):
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
mysql_error_code = ERR.ER_UNKNOWN_ERROR
|
|
7
|
+
is_expected = False
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
# base exception for known error
|
|
10
11
|
class ExecutorException(Exception):
|
|
11
|
-
|
|
12
|
+
mysql_error_code = ERR.ER_UNKNOWN_ERROR
|
|
13
|
+
is_expected = False
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
class NotSupportedYet(ExecutorException):
|
|
15
|
-
|
|
17
|
+
mysql_error_code = ERR.ER_NOT_SUPPORTED_YET
|
|
18
|
+
is_expected = True
|
|
16
19
|
|
|
17
20
|
|
|
18
21
|
class BadDbError(ExecutorException):
|
|
19
|
-
|
|
22
|
+
mysql_error_code = ERR.ER_BAD_DB_ERROR
|
|
23
|
+
is_expected = True
|
|
20
24
|
|
|
21
25
|
|
|
22
26
|
class BadTableError(ExecutorException):
|
|
23
|
-
|
|
27
|
+
mysql_error_code = ERR.ER_BAD_DB_ERROR
|
|
28
|
+
is_expected = True
|
|
24
29
|
|
|
25
30
|
|
|
26
31
|
class KeyColumnDoesNotExist(ExecutorException):
|
|
27
|
-
|
|
32
|
+
mysql_error_code = ERR.ER_KEY_COLUMN_DOES_NOT_EXIST
|
|
33
|
+
is_expected = True
|
|
28
34
|
|
|
29
35
|
|
|
30
36
|
class TableNotExistError(ExecutorException):
|
|
31
|
-
|
|
37
|
+
mysql_error_code = ERR.ER_TABLE_EXISTS_ERROR
|
|
38
|
+
is_expected = True
|
|
32
39
|
|
|
33
40
|
|
|
34
41
|
class WrongArgumentError(ExecutorException):
|
|
35
|
-
|
|
42
|
+
mysql_error_code = ERR.ER_WRONG_ARGUMENTS
|
|
43
|
+
is_expected = True
|
|
36
44
|
|
|
37
45
|
|
|
38
46
|
class LogicError(ExecutorException):
|
|
39
|
-
|
|
47
|
+
mysql_error_code = ERR.ER_WRONG_USAGE
|
|
48
|
+
is_expected = True
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class SqlSyntaxError(ExecutorException):
|
|
52
|
+
err_code = ERR.ER_SYNTAX_ERROR
|
|
53
|
+
is_expected = True
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class WrongCharsetError(ExecutorException):
|
|
57
|
+
err_code = ERR.ER_UNKNOWN_CHARACTER_SET
|
|
58
|
+
is_expected = True
|
|
@@ -13,6 +13,8 @@ from mindsdb_sql_parser.ast import (
|
|
|
13
13
|
Constant,
|
|
14
14
|
NativeQuery,
|
|
15
15
|
Parameter,
|
|
16
|
+
Function,
|
|
17
|
+
Last,
|
|
16
18
|
)
|
|
17
19
|
|
|
18
20
|
from mindsdb.integrations.utilities.query_traversal import query_traversal
|
|
@@ -220,17 +222,16 @@ class PlanJoinTablesQuery:
|
|
|
220
222
|
# try to use second arg, could be: 'x'=col
|
|
221
223
|
col_idx = 1
|
|
222
224
|
|
|
223
|
-
# check the case col <condition>
|
|
225
|
+
# check the case col <condition> value, col between value and value
|
|
224
226
|
for i, arg in enumerate(node.args):
|
|
225
227
|
if i == col_idx:
|
|
226
228
|
if not isinstance(arg, Identifier):
|
|
227
229
|
return
|
|
228
230
|
else:
|
|
229
|
-
if not
|
|
231
|
+
if not self.can_be_table_filter(arg):
|
|
230
232
|
return
|
|
231
233
|
|
|
232
234
|
# checked, find table and store condition
|
|
233
|
-
|
|
234
235
|
node2 = copy.deepcopy(node)
|
|
235
236
|
|
|
236
237
|
arg1 = node2.args[col_idx]
|
|
@@ -248,6 +249,19 @@ class PlanJoinTablesQuery:
|
|
|
248
249
|
node2._orig_node = node
|
|
249
250
|
table_info.conditions.append(node2)
|
|
250
251
|
|
|
252
|
+
def can_be_table_filter(self, node):
|
|
253
|
+
"""
|
|
254
|
+
Check if node can be used as a filter.
|
|
255
|
+
It can contain only: Constant, Parameter, Function (with Last)
|
|
256
|
+
"""
|
|
257
|
+
if isinstance(node, (Constant, Parameter)):
|
|
258
|
+
return True
|
|
259
|
+
if isinstance(node, Function):
|
|
260
|
+
# `Last` must be in args
|
|
261
|
+
if not any(isinstance(arg, Last) for arg in node.args):
|
|
262
|
+
return False
|
|
263
|
+
return all([self.can_be_table_filter(arg) for arg in node.args])
|
|
264
|
+
|
|
251
265
|
def check_query_conditions(self, query):
|
|
252
266
|
# get conditions for tables
|
|
253
267
|
binary_ops = []
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* permission of MindsDB Inc
|
|
9
9
|
*******************************************************
|
|
10
10
|
"""
|
|
11
|
+
|
|
11
12
|
import inspect
|
|
12
13
|
from textwrap import dedent
|
|
13
14
|
from typing import Union, Dict
|
|
@@ -41,15 +42,21 @@ from mindsdb.utilities.context import context as ctx
|
|
|
41
42
|
|
|
42
43
|
from . import steps
|
|
43
44
|
from .result_set import ResultSet, Column
|
|
44
|
-
from .
|
|
45
|
+
from .steps.base import BaseStepCall
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
class SQLQuery:
|
|
48
|
-
|
|
49
49
|
step_handlers = {}
|
|
50
50
|
|
|
51
|
-
def __init__(
|
|
52
|
-
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
sql: Union[ASTNode, str],
|
|
54
|
+
session,
|
|
55
|
+
execute: bool = True,
|
|
56
|
+
database: str = None,
|
|
57
|
+
query_id: int = None,
|
|
58
|
+
stop_event=None,
|
|
59
|
+
):
|
|
53
60
|
self.session = session
|
|
54
61
|
|
|
55
62
|
self.query_id = query_id
|
|
@@ -64,10 +71,7 @@ class SQLQuery:
|
|
|
64
71
|
else:
|
|
65
72
|
self.database = session.database
|
|
66
73
|
|
|
67
|
-
self.context = {
|
|
68
|
-
'database': None if self.database == '' else self.database.lower(),
|
|
69
|
-
'row_id': 0
|
|
70
|
-
}
|
|
74
|
+
self.context = {"database": None if self.database == "" else self.database.lower(), "row_id": 0}
|
|
71
75
|
|
|
72
76
|
self.columns_list = None
|
|
73
77
|
self.steps_data: Dict[int, ResultSet] = {}
|
|
@@ -82,14 +86,14 @@ class SQLQuery:
|
|
|
82
86
|
|
|
83
87
|
if isinstance(sql, str):
|
|
84
88
|
self.query = parse_sql(sql)
|
|
85
|
-
self.context[
|
|
89
|
+
self.context["query_str"] = sql
|
|
86
90
|
else:
|
|
87
91
|
self.query = sql
|
|
88
|
-
renderer = SqlalchemyRender(
|
|
92
|
+
renderer = SqlalchemyRender("mysql")
|
|
89
93
|
try:
|
|
90
|
-
self.context[
|
|
94
|
+
self.context["query_str"] = renderer.get_string(self.query, with_failback=True)
|
|
91
95
|
except Exception:
|
|
92
|
-
self.context[
|
|
96
|
+
self.context["query_str"] = str(self.query)
|
|
93
97
|
|
|
94
98
|
self.create_planner()
|
|
95
99
|
|
|
@@ -98,7 +102,6 @@ class SQLQuery:
|
|
|
98
102
|
|
|
99
103
|
@classmethod
|
|
100
104
|
def register_steps(cls):
|
|
101
|
-
|
|
102
105
|
cls.step_handlers = {}
|
|
103
106
|
for _, cl in inspect.getmembers(steps):
|
|
104
107
|
if inspect.isclass(cl) and issubclass(cl, BaseStepCall):
|
|
@@ -115,13 +118,10 @@ class SQLQuery:
|
|
|
115
118
|
query_tables = get_query_models(self.query, default_database=self.database)
|
|
116
119
|
|
|
117
120
|
for project_name, table_name, table_version in query_tables:
|
|
118
|
-
args = {
|
|
119
|
-
'name': table_name,
|
|
120
|
-
'project_name': project_name
|
|
121
|
-
}
|
|
121
|
+
args = {"name": table_name, "project_name": project_name}
|
|
122
122
|
if table_version is not None:
|
|
123
|
-
args[
|
|
124
|
-
args[
|
|
123
|
+
args["active"] = None
|
|
124
|
+
args["version"] = table_version
|
|
125
125
|
|
|
126
126
|
model_record = get_model_record(**args)
|
|
127
127
|
if model_record is None:
|
|
@@ -132,61 +132,65 @@ class SQLQuery:
|
|
|
132
132
|
continue
|
|
133
133
|
if agent is not None:
|
|
134
134
|
predictor = {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
135
|
+
"name": table_name,
|
|
136
|
+
"integration_name": project_name, # integration_name,
|
|
137
|
+
"timeseries": False,
|
|
138
|
+
"id": agent.id,
|
|
139
|
+
"to_predict": "answer",
|
|
140
140
|
}
|
|
141
141
|
predictor_metadata.append(predictor)
|
|
142
142
|
|
|
143
143
|
continue
|
|
144
144
|
|
|
145
|
-
if model_record.status ==
|
|
146
|
-
dot_version_str =
|
|
147
|
-
and_version_str =
|
|
145
|
+
if model_record.status == "error":
|
|
146
|
+
dot_version_str = ""
|
|
147
|
+
and_version_str = ""
|
|
148
148
|
if table_version is not None:
|
|
149
|
-
dot_version_str = f
|
|
150
|
-
and_version_str = f
|
|
149
|
+
dot_version_str = f".{table_version}"
|
|
150
|
+
and_version_str = f" and version = {table_version}"
|
|
151
151
|
|
|
152
|
-
raise BadTableError(
|
|
152
|
+
raise BadTableError(
|
|
153
|
+
dedent(f"""\
|
|
153
154
|
The model '{table_name}{dot_version_str}' cannot be used as it is currently in 'error' status.
|
|
154
155
|
For detailed information about the error, please execute the following command:
|
|
155
156
|
|
|
156
157
|
select error from information_schema.models where name = '{table_name}'{and_version_str};
|
|
157
|
-
|
|
158
|
+
""")
|
|
159
|
+
)
|
|
158
160
|
|
|
159
|
-
ts_settings = model_record.learn_args.get(
|
|
161
|
+
ts_settings = model_record.learn_args.get("timeseries_settings", {})
|
|
160
162
|
predictor = {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
"name": table_name,
|
|
164
|
+
"integration_name": project_name, # integration_name,
|
|
165
|
+
"timeseries": False,
|
|
166
|
+
"id": model_record.id,
|
|
167
|
+
"to_predict": model_record.to_predict,
|
|
166
168
|
}
|
|
167
|
-
if ts_settings.get(
|
|
168
|
-
window = ts_settings.get(
|
|
169
|
-
order_by = ts_settings.get(
|
|
169
|
+
if ts_settings.get("is_timeseries") is True:
|
|
170
|
+
window = ts_settings.get("window")
|
|
171
|
+
order_by = ts_settings.get("order_by")
|
|
170
172
|
if isinstance(order_by, list):
|
|
171
173
|
order_by = order_by[0]
|
|
172
|
-
group_by = ts_settings.get(
|
|
174
|
+
group_by = ts_settings.get("group_by")
|
|
173
175
|
if isinstance(group_by, list) is False and group_by is not None:
|
|
174
176
|
group_by = [group_by]
|
|
175
|
-
predictor.update(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
177
|
+
predictor.update(
|
|
178
|
+
{
|
|
179
|
+
"timeseries": True,
|
|
180
|
+
"window": window,
|
|
181
|
+
"horizon": ts_settings.get("horizon"),
|
|
182
|
+
"order_by_column": order_by,
|
|
183
|
+
"group_by_columns": group_by,
|
|
184
|
+
}
|
|
185
|
+
)
|
|
182
186
|
|
|
183
|
-
predictor[
|
|
187
|
+
predictor["model_types"] = model_record.data.get("dtypes", {})
|
|
184
188
|
|
|
185
189
|
predictor_metadata.append(predictor)
|
|
186
190
|
|
|
187
|
-
database = None if self.database ==
|
|
191
|
+
database = None if self.database == "" else self.database.lower()
|
|
188
192
|
|
|
189
|
-
self.context[
|
|
193
|
+
self.context["predictor_metadata"] = predictor_metadata
|
|
190
194
|
self.planner = query_planner.QueryPlanner(
|
|
191
195
|
self.query,
|
|
192
196
|
integrations=databases,
|
|
@@ -195,38 +199,32 @@ class SQLQuery:
|
|
|
195
199
|
)
|
|
196
200
|
|
|
197
201
|
def prepare_query(self):
|
|
198
|
-
"""it is prepared statement call
|
|
199
|
-
"""
|
|
202
|
+
"""it is prepared statement call"""
|
|
200
203
|
try:
|
|
201
204
|
for step in self.planner.prepare_steps(self.query):
|
|
202
205
|
data = self.execute_step(step)
|
|
203
206
|
step.set_result(data)
|
|
204
207
|
self.steps_data[step.step_num] = data
|
|
205
208
|
except PlanningException as e:
|
|
206
|
-
raise LogicError(e)
|
|
209
|
+
raise LogicError(e) from e
|
|
207
210
|
|
|
208
211
|
statement_info = self.planner.get_statement_info()
|
|
209
212
|
|
|
210
213
|
self.columns_list = []
|
|
211
|
-
for col in statement_info[
|
|
214
|
+
for col in statement_info["columns"]:
|
|
212
215
|
self.columns_list.append(
|
|
213
216
|
Column(
|
|
214
|
-
database=col[
|
|
215
|
-
table_name=col[
|
|
216
|
-
table_alias=col[
|
|
217
|
-
name=col[
|
|
218
|
-
alias=col[
|
|
219
|
-
type=col[
|
|
217
|
+
database=col["ds"],
|
|
218
|
+
table_name=col["table_name"],
|
|
219
|
+
table_alias=col["table_alias"],
|
|
220
|
+
name=col["name"],
|
|
221
|
+
alias=col["alias"],
|
|
222
|
+
type=col["type"],
|
|
220
223
|
)
|
|
221
224
|
)
|
|
222
225
|
|
|
223
226
|
self.parameters = [
|
|
224
|
-
Column(
|
|
225
|
-
name=col['name'],
|
|
226
|
-
alias=col['alias'],
|
|
227
|
-
type=col['type']
|
|
228
|
-
)
|
|
229
|
-
for col in statement_info['parameters']
|
|
227
|
+
Column(name=col["name"], alias=col["alias"], type=col["type"]) for col in statement_info["parameters"]
|
|
230
228
|
]
|
|
231
229
|
|
|
232
230
|
def execute_query(self):
|
|
@@ -237,14 +235,16 @@ class SQLQuery:
|
|
|
237
235
|
try:
|
|
238
236
|
steps = list(self.planner.execute_steps())
|
|
239
237
|
except PlanningException as e:
|
|
240
|
-
raise LogicError(e)
|
|
238
|
+
raise LogicError(e) from e
|
|
241
239
|
|
|
242
240
|
if self.planner.plan.is_resumable:
|
|
243
241
|
# create query
|
|
244
242
|
if self.query_id is not None:
|
|
245
243
|
self.run_query = query_context_controller.get_query(self.query_id)
|
|
246
244
|
else:
|
|
247
|
-
self.run_query = query_context_controller.create_query(
|
|
245
|
+
self.run_query = query_context_controller.create_query(
|
|
246
|
+
self.context["query_str"], database=self.database
|
|
247
|
+
)
|
|
248
248
|
|
|
249
249
|
if self.planner.plan.is_async and ctx.task_id is None:
|
|
250
250
|
# add to task
|
|
@@ -265,9 +265,9 @@ class SQLQuery:
|
|
|
265
265
|
steps_classes = (x.__class__ for x in steps)
|
|
266
266
|
predict_steps = (ApplyPredictorRowStep, ApplyPredictorStep, ApplyTimeseriesPredictorStep)
|
|
267
267
|
if any(s in predict_steps for s in steps_classes):
|
|
268
|
-
process_mark = create_process_mark(
|
|
268
|
+
process_mark = create_process_mark("predict")
|
|
269
269
|
for step in steps:
|
|
270
|
-
with profiler.Context(f
|
|
270
|
+
with profiler.Context(f"step: {step.__class__.__name__}"):
|
|
271
271
|
step_result = self.execute_step(step)
|
|
272
272
|
self.steps_data[step.step_num] = step_result
|
|
273
273
|
except Exception as e:
|
|
@@ -282,7 +282,7 @@ class SQLQuery:
|
|
|
282
282
|
ctx.run_query_id = None
|
|
283
283
|
finally:
|
|
284
284
|
if process_mark is not None:
|
|
285
|
-
delete_process_mark(
|
|
285
|
+
delete_process_mark("predict", process_mark)
|
|
286
286
|
|
|
287
287
|
# save updated query
|
|
288
288
|
self.query = self.planner.query
|
|
@@ -294,14 +294,14 @@ class SQLQuery:
|
|
|
294
294
|
self.fetched_data = step_result
|
|
295
295
|
|
|
296
296
|
try:
|
|
297
|
-
if hasattr(self,
|
|
297
|
+
if hasattr(self, "columns_list") is False:
|
|
298
298
|
# how it becomes False?
|
|
299
299
|
self.columns_list = self.fetched_data.columns
|
|
300
300
|
|
|
301
301
|
if self.columns_list is None:
|
|
302
302
|
self.columns_list = self.fetched_data.columns
|
|
303
303
|
|
|
304
|
-
for col in self.fetched_data.find_columns(
|
|
304
|
+
for col in self.fetched_data.find_columns("__mindsdb_row_id"):
|
|
305
305
|
self.fetched_data.del_column(col)
|
|
306
306
|
|
|
307
307
|
except Exception as e:
|
|
@@ -87,8 +87,7 @@ class FetchDataframeStepCall(BaseStepCall):
|
|
|
87
87
|
if query is None:
|
|
88
88
|
table_alias = (self.context.get("database"), "result", "result")
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
response: DataHubResponse = dn.query(native_query=step.raw_query, session=self.session)
|
|
90
|
+
response: DataHubResponse = dn.query(step.raw_query, session=self.session)
|
|
92
91
|
df = response.data_frame
|
|
93
92
|
else:
|
|
94
93
|
if isinstance(step.query, (Union, Intersect)):
|
|
@@ -199,7 +199,6 @@ class QueryStepCall(BaseStepCall):
|
|
|
199
199
|
# but can be absent in their output
|
|
200
200
|
|
|
201
201
|
def remove_not_used_conditions(node, **kwargs):
|
|
202
|
-
# find last in where
|
|
203
202
|
if isinstance(node, BinaryOperation):
|
|
204
203
|
for arg in node.args:
|
|
205
204
|
if isinstance(arg, Identifier) and len(arg.parts) > 1:
|
|
@@ -23,15 +23,15 @@ def download_file(url):
|
|
|
23
23
|
parse_result = urllib.parse.urlparse(url)
|
|
24
24
|
scheme = parse_result.scheme
|
|
25
25
|
except ValueError:
|
|
26
|
-
raise Exception(f
|
|
26
|
+
raise Exception(f"Invalid url: {url}")
|
|
27
27
|
except Exception as e:
|
|
28
|
-
raise Exception(f
|
|
29
|
-
temp_dir = tempfile.mkdtemp(prefix=
|
|
30
|
-
if scheme ==
|
|
28
|
+
raise Exception(f"URL parsing error: {e}") from e
|
|
29
|
+
temp_dir = tempfile.mkdtemp(prefix="mindsdb_file_download_")
|
|
30
|
+
if scheme == "":
|
|
31
31
|
raise Exception(f"Unknown url schema: {url}")
|
|
32
32
|
|
|
33
33
|
response = requests.get(url)
|
|
34
|
-
temp_file_path = Path(temp_dir).joinpath(
|
|
35
|
-
with open(str(temp_file_path),
|
|
34
|
+
temp_file_path = Path(temp_dir).joinpath("file")
|
|
35
|
+
with open(str(temp_file_path), "wb") as file:
|
|
36
36
|
file.write(response.content)
|
|
37
37
|
return str(temp_file_path)
|
|
@@ -10,7 +10,7 @@ from mindsdb_sql_parser.ast import ASTNode, Select, Identifier, Function, Consta
|
|
|
10
10
|
|
|
11
11
|
from mindsdb.integrations.utilities.query_traversal import query_traversal
|
|
12
12
|
from mindsdb.utilities import log
|
|
13
|
-
from mindsdb.utilities.exception import
|
|
13
|
+
from mindsdb.utilities.exception import QueryError
|
|
14
14
|
from mindsdb.utilities.functions import resolve_table_identifier, resolve_model_identifier
|
|
15
15
|
from mindsdb.utilities.json_encoder import CustomJSONEncoder
|
|
16
16
|
from mindsdb.utilities.render.sqlalchemy_render import SqlalchemyRender
|
|
@@ -64,6 +64,9 @@ def query_df_with_type_infer_fallback(query_str: str, dataframes: dict, user_fun
|
|
|
64
64
|
Returns:
|
|
65
65
|
pandas.DataFrame
|
|
66
66
|
pandas.columns
|
|
67
|
+
|
|
68
|
+
Raises:
|
|
69
|
+
QueryError: Raised when DuckDB fails to execute the query
|
|
67
70
|
"""
|
|
68
71
|
|
|
69
72
|
try:
|
|
@@ -86,9 +89,17 @@ def query_df_with_type_infer_fallback(query_str: str, dataframes: dict, user_fun
|
|
|
86
89
|
else:
|
|
87
90
|
raise exception
|
|
88
91
|
description = con.description
|
|
92
|
+
except InvalidInputException as e:
|
|
93
|
+
raise QueryError(
|
|
94
|
+
db_type="DuckDB",
|
|
95
|
+
db_error_msg=f"DuckDB failed to execute query, likely due to inability to determine column data types. Details: {e}",
|
|
96
|
+
failed_query=query_str,
|
|
97
|
+
is_external=False,
|
|
98
|
+
is_expected=False,
|
|
99
|
+
) from e
|
|
89
100
|
except Exception as e:
|
|
90
|
-
raise
|
|
91
|
-
|
|
101
|
+
raise QueryError(
|
|
102
|
+
db_type="DuckDB", db_error_msg=str(e), failed_query=query_str, is_external=False, is_expected=False
|
|
92
103
|
) from e
|
|
93
104
|
|
|
94
105
|
return result_df, description
|
|
@@ -164,7 +175,13 @@ def query_df(df, query, session=None):
|
|
|
164
175
|
query_str = str(query)
|
|
165
176
|
|
|
166
177
|
if isinstance(query_ast, Select) is False or isinstance(query_ast.from_table, Identifier) is False:
|
|
167
|
-
raise
|
|
178
|
+
raise QueryError(
|
|
179
|
+
db_type="DuckDB",
|
|
180
|
+
db_error_msg="Only 'SELECT from TABLE' statements supported for internal query",
|
|
181
|
+
failed_query=query_str,
|
|
182
|
+
is_external=False,
|
|
183
|
+
is_expected=False,
|
|
184
|
+
)
|
|
168
185
|
|
|
169
186
|
table_name = query_ast.from_table.parts[0]
|
|
170
187
|
query_ast.from_table.parts = ["df"]
|
|
@@ -214,16 +231,15 @@ def query_df(df, query, session=None):
|
|
|
214
231
|
custom_functions_list = [] if user_functions is None else list(user_functions.functions.keys())
|
|
215
232
|
all_functions_list = duckdb_functions_and_kw_list + custom_functions_list
|
|
216
233
|
if len(all_functions_list) > 0 and fnc_name not in all_functions_list:
|
|
217
|
-
raise
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
)
|
|
234
|
+
raise QueryError(
|
|
235
|
+
db_type="DuckDB",
|
|
236
|
+
db_error_msg=(
|
|
237
|
+
f"Unknown function: '{fnc_name}'. This function is not recognized during internal query processing.\n"
|
|
238
|
+
"Please use DuckDB-supported functions instead."
|
|
239
|
+
),
|
|
240
|
+
failed_query=query_str,
|
|
241
|
+
is_external=False,
|
|
242
|
+
is_expected=False,
|
|
227
243
|
)
|
|
228
244
|
|
|
229
245
|
query_traversal(query_ast, adapt_query)
|
|
@@ -245,8 +261,8 @@ def query_df(df, query, session=None):
|
|
|
245
261
|
render = SqlalchemyRender("postgres")
|
|
246
262
|
try:
|
|
247
263
|
query_str = render.get_string(query_ast, with_failback=False)
|
|
248
|
-
except Exception
|
|
249
|
-
logger.
|
|
264
|
+
except Exception:
|
|
265
|
+
logger.exception(f"Exception during query casting to 'postgres' dialect. Query:\n{str(query)}.\nError:")
|
|
250
266
|
query_str = render.get_string(query_ast, with_failback=True)
|
|
251
267
|
|
|
252
268
|
# workaround to prevent duckdb.TypeMismatchException
|
mindsdb/api/http/gui.py
CHANGED
|
@@ -30,8 +30,8 @@ def download_gui(destignation, version):
|
|
|
30
30
|
try:
|
|
31
31
|
for r in resources:
|
|
32
32
|
get_resources(r)
|
|
33
|
-
except Exception
|
|
34
|
-
logger.
|
|
33
|
+
except Exception:
|
|
34
|
+
logger.exception("Error during downloading files from s3:")
|
|
35
35
|
return False
|
|
36
36
|
|
|
37
37
|
static_folder = destignation
|
|
@@ -39,12 +39,8 @@ def download_gui(destignation, version):
|
|
|
39
39
|
ZipFile(dist_zip_path).extractall(static_folder)
|
|
40
40
|
|
|
41
41
|
if static_folder.joinpath("dist").is_dir():
|
|
42
|
-
shutil.move(
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
shutil.move(
|
|
46
|
-
str(destignation.joinpath("dist").joinpath("assets")), static_folder
|
|
47
|
-
)
|
|
42
|
+
shutil.move(str(destignation.joinpath("dist").joinpath("index.html")), static_folder)
|
|
43
|
+
shutil.move(str(destignation.joinpath("dist").joinpath("assets")), static_folder)
|
|
48
44
|
shutil.rmtree(destignation.joinpath("dist"))
|
|
49
45
|
|
|
50
46
|
os.remove(dist_zip_path)
|
|
@@ -74,9 +70,7 @@ def update_static(gui_version: Version):
|
|
|
74
70
|
config = Config()
|
|
75
71
|
static_path = Path(config["paths"]["static"])
|
|
76
72
|
|
|
77
|
-
logger.info(
|
|
78
|
-
f"New version of GUI available ({gui_version.base_version}). Downloading..."
|
|
79
|
-
)
|
|
73
|
+
logger.info(f"New version of GUI available ({gui_version.base_version}). Downloading...")
|
|
80
74
|
|
|
81
75
|
temp_dir = tempfile.mkdtemp(prefix="mindsdb_gui_files_")
|
|
82
76
|
success = download_gui(temp_dir, gui_version.base_version)
|
mindsdb/api/http/initialize.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import mimetypes
|
|
3
3
|
import threading
|
|
4
|
-
import traceback
|
|
5
4
|
import webbrowser
|
|
6
5
|
|
|
7
6
|
from pathlib import Path
|
|
@@ -154,8 +153,8 @@ def get_last_compatible_gui_version() -> Version | bool:
|
|
|
154
153
|
else:
|
|
155
154
|
all_lower_versions = [parse_version(x) for x in lower_versions.keys()]
|
|
156
155
|
gui_version_lv = gui_versions[all_lower_versions[-1].base_version]
|
|
157
|
-
except Exception
|
|
158
|
-
logger.
|
|
156
|
+
except Exception:
|
|
157
|
+
logger.exception("Error in compatible-config.json structure:")
|
|
159
158
|
return False
|
|
160
159
|
|
|
161
160
|
logger.debug(f"Last compatible frontend version: {gui_version_lv}.")
|
|
@@ -349,15 +348,15 @@ def initialize_app():
|
|
|
349
348
|
if company_id is not None:
|
|
350
349
|
try:
|
|
351
350
|
company_id = int(company_id)
|
|
352
|
-
except Exception
|
|
353
|
-
logger.
|
|
351
|
+
except Exception:
|
|
352
|
+
logger.exception(f"Could not parse company id: {company_id} | exception:")
|
|
354
353
|
company_id = None
|
|
355
354
|
|
|
356
355
|
if user_class is not None:
|
|
357
356
|
try:
|
|
358
357
|
user_class = int(user_class)
|
|
359
|
-
except Exception
|
|
360
|
-
logger.
|
|
358
|
+
except Exception:
|
|
359
|
+
logger.exception(f"Could not parse user_class: {user_class} | exception:")
|
|
361
360
|
user_class = 0
|
|
362
361
|
else:
|
|
363
362
|
user_class = 0
|
|
@@ -449,7 +448,6 @@ def _open_webbrowser(url: str, pid: int, port: int, init_static_thread, static_f
|
|
|
449
448
|
is_http_active = wait_func_is_true(func=is_pid_listen_port, timeout=15, pid=pid, port=port)
|
|
450
449
|
if is_http_active:
|
|
451
450
|
webbrowser.open(url)
|
|
452
|
-
except Exception
|
|
453
|
-
logger.
|
|
454
|
-
logger.error(traceback.format_exc())
|
|
451
|
+
except Exception:
|
|
452
|
+
logger.exception(f"Failed to open {url} in webbrowser with exception:")
|
|
455
453
|
db.session.close()
|