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.

Files changed (120) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +39 -20
  3. mindsdb/api/a2a/agent.py +7 -9
  4. mindsdb/api/a2a/common/server/server.py +3 -3
  5. mindsdb/api/a2a/common/server/task_manager.py +4 -4
  6. mindsdb/api/a2a/task_manager.py +15 -17
  7. mindsdb/api/common/middleware.py +9 -11
  8. mindsdb/api/executor/command_executor.py +2 -4
  9. mindsdb/api/executor/datahub/datanodes/datanode.py +2 -2
  10. mindsdb/api/executor/datahub/datanodes/integration_datanode.py +100 -48
  11. mindsdb/api/executor/datahub/datanodes/project_datanode.py +8 -4
  12. mindsdb/api/executor/datahub/datanodes/system_tables.py +1 -1
  13. mindsdb/api/executor/exceptions.py +29 -10
  14. mindsdb/api/executor/planner/plan_join.py +17 -3
  15. mindsdb/api/executor/sql_query/sql_query.py +74 -74
  16. mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +1 -2
  17. mindsdb/api/executor/sql_query/steps/subselect_step.py +0 -1
  18. mindsdb/api/executor/utilities/functions.py +6 -6
  19. mindsdb/api/executor/utilities/sql.py +32 -16
  20. mindsdb/api/http/gui.py +5 -11
  21. mindsdb/api/http/initialize.py +8 -10
  22. mindsdb/api/http/namespaces/agents.py +10 -12
  23. mindsdb/api/http/namespaces/analysis.py +13 -20
  24. mindsdb/api/http/namespaces/auth.py +1 -1
  25. mindsdb/api/http/namespaces/config.py +15 -11
  26. mindsdb/api/http/namespaces/databases.py +140 -201
  27. mindsdb/api/http/namespaces/file.py +15 -4
  28. mindsdb/api/http/namespaces/handlers.py +7 -2
  29. mindsdb/api/http/namespaces/knowledge_bases.py +8 -7
  30. mindsdb/api/http/namespaces/models.py +94 -126
  31. mindsdb/api/http/namespaces/projects.py +13 -22
  32. mindsdb/api/http/namespaces/sql.py +33 -25
  33. mindsdb/api/http/namespaces/tab.py +27 -37
  34. mindsdb/api/http/namespaces/views.py +1 -1
  35. mindsdb/api/http/start.py +14 -8
  36. mindsdb/api/mcp/__init__.py +2 -1
  37. mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +15 -20
  38. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +26 -50
  39. mindsdb/api/mysql/mysql_proxy/utilities/__init__.py +0 -1
  40. mindsdb/api/postgres/postgres_proxy/executor/executor.py +6 -13
  41. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_packets.py +40 -28
  42. mindsdb/integrations/handlers/byom_handler/byom_handler.py +168 -185
  43. mindsdb/integrations/handlers/chromadb_handler/chromadb_handler.py +11 -5
  44. mindsdb/integrations/handlers/file_handler/file_handler.py +7 -0
  45. mindsdb/integrations/handlers/lightwood_handler/functions.py +45 -79
  46. mindsdb/integrations/handlers/openai_handler/openai_handler.py +1 -1
  47. mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +20 -2
  48. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +18 -3
  49. mindsdb/integrations/handlers/shopify_handler/shopify_handler.py +25 -12
  50. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +2 -1
  51. mindsdb/integrations/handlers/statsforecast_handler/requirements.txt +1 -0
  52. mindsdb/integrations/handlers/statsforecast_handler/requirements_extra.txt +1 -0
  53. mindsdb/integrations/handlers/web_handler/urlcrawl_helpers.py +4 -4
  54. mindsdb/integrations/libs/api_handler.py +10 -10
  55. mindsdb/integrations/libs/base.py +4 -4
  56. mindsdb/integrations/libs/llm/utils.py +2 -2
  57. mindsdb/integrations/libs/ml_handler_process/create_engine_process.py +4 -7
  58. mindsdb/integrations/libs/ml_handler_process/func_call_process.py +2 -7
  59. mindsdb/integrations/libs/ml_handler_process/learn_process.py +37 -47
  60. mindsdb/integrations/libs/ml_handler_process/update_engine_process.py +4 -7
  61. mindsdb/integrations/libs/ml_handler_process/update_process.py +2 -7
  62. mindsdb/integrations/libs/process_cache.py +132 -140
  63. mindsdb/integrations/libs/response.py +18 -12
  64. mindsdb/integrations/libs/vectordatabase_handler.py +26 -0
  65. mindsdb/integrations/utilities/files/file_reader.py +6 -7
  66. mindsdb/integrations/utilities/rag/config_loader.py +37 -26
  67. mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +59 -9
  68. mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +4 -4
  69. mindsdb/integrations/utilities/rag/retrievers/sql_retriever.py +55 -133
  70. mindsdb/integrations/utilities/rag/settings.py +58 -133
  71. mindsdb/integrations/utilities/rag/splitters/file_splitter.py +5 -15
  72. mindsdb/interfaces/agents/agents_controller.py +2 -1
  73. mindsdb/interfaces/agents/constants.py +0 -2
  74. mindsdb/interfaces/agents/litellm_server.py +34 -58
  75. mindsdb/interfaces/agents/mcp_client_agent.py +10 -10
  76. mindsdb/interfaces/agents/mindsdb_database_agent.py +5 -5
  77. mindsdb/interfaces/agents/run_mcp_agent.py +12 -21
  78. mindsdb/interfaces/chatbot/chatbot_task.py +20 -23
  79. mindsdb/interfaces/chatbot/polling.py +30 -18
  80. mindsdb/interfaces/data_catalog/data_catalog_loader.py +10 -10
  81. mindsdb/interfaces/database/integrations.py +19 -2
  82. mindsdb/interfaces/file/file_controller.py +6 -6
  83. mindsdb/interfaces/functions/controller.py +1 -1
  84. mindsdb/interfaces/functions/to_markdown.py +2 -2
  85. mindsdb/interfaces/jobs/jobs_controller.py +5 -5
  86. mindsdb/interfaces/jobs/scheduler.py +3 -8
  87. mindsdb/interfaces/knowledge_base/controller.py +54 -25
  88. mindsdb/interfaces/knowledge_base/preprocessing/json_chunker.py +40 -61
  89. mindsdb/interfaces/model/model_controller.py +170 -166
  90. mindsdb/interfaces/query_context/context_controller.py +14 -2
  91. mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +6 -4
  92. mindsdb/interfaces/skills/retrieval_tool.py +43 -50
  93. mindsdb/interfaces/skills/skill_tool.py +2 -2
  94. mindsdb/interfaces/skills/sql_agent.py +25 -19
  95. mindsdb/interfaces/storage/fs.py +114 -169
  96. mindsdb/interfaces/storage/json.py +19 -18
  97. mindsdb/interfaces/storage/model_fs.py +54 -92
  98. mindsdb/interfaces/tabs/tabs_controller.py +49 -72
  99. mindsdb/interfaces/tasks/task_monitor.py +3 -9
  100. mindsdb/interfaces/tasks/task_thread.py +7 -9
  101. mindsdb/interfaces/triggers/trigger_task.py +7 -13
  102. mindsdb/interfaces/triggers/triggers_controller.py +47 -50
  103. mindsdb/migrations/migrate.py +16 -16
  104. mindsdb/utilities/api_status.py +58 -0
  105. mindsdb/utilities/config.py +49 -0
  106. mindsdb/utilities/exception.py +40 -1
  107. mindsdb/utilities/fs.py +0 -1
  108. mindsdb/utilities/hooks/profiling.py +17 -14
  109. mindsdb/utilities/langfuse.py +40 -45
  110. mindsdb/utilities/log.py +272 -0
  111. mindsdb/utilities/ml_task_queue/consumer.py +52 -58
  112. mindsdb/utilities/ml_task_queue/producer.py +26 -30
  113. mindsdb/utilities/render/sqlalchemy_render.py +8 -7
  114. mindsdb/utilities/utils.py +2 -2
  115. {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/METADATA +266 -261
  116. {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/RECORD +119 -119
  117. mindsdb/api/mysql/mysql_proxy/utilities/exceptions.py +0 -14
  118. {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/WHEEL +0 -0
  119. {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/licenses/LICENSE +0 -0
  120. {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
- # err_code = ERR.ER_UNKNOWN_ERROR
6
- pass
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
- pass
12
+ mysql_error_code = ERR.ER_UNKNOWN_ERROR
13
+ is_expected = False
12
14
 
13
15
 
14
16
  class NotSupportedYet(ExecutorException):
15
- pass
17
+ mysql_error_code = ERR.ER_NOT_SUPPORTED_YET
18
+ is_expected = True
16
19
 
17
20
 
18
21
  class BadDbError(ExecutorException):
19
- pass
22
+ mysql_error_code = ERR.ER_BAD_DB_ERROR
23
+ is_expected = True
20
24
 
21
25
 
22
26
  class BadTableError(ExecutorException):
23
- pass
27
+ mysql_error_code = ERR.ER_BAD_DB_ERROR
28
+ is_expected = True
24
29
 
25
30
 
26
31
  class KeyColumnDoesNotExist(ExecutorException):
27
- pass
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
- pass
37
+ mysql_error_code = ERR.ER_TABLE_EXISTS_ERROR
38
+ is_expected = True
32
39
 
33
40
 
34
41
  class WrongArgumentError(ExecutorException):
35
- pass
42
+ mysql_error_code = ERR.ER_WRONG_ARGUMENTS
43
+ is_expected = True
36
44
 
37
45
 
38
46
  class LogicError(ExecutorException):
39
- pass
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> constant, col between constant and constant
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 isinstance(arg, (Constant, Parameter)):
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 . steps.base import BaseStepCall
45
+ from .steps.base import BaseStepCall
45
46
 
46
47
 
47
48
  class SQLQuery:
48
-
49
49
  step_handlers = {}
50
50
 
51
- def __init__(self, sql: Union[ASTNode, str], session, execute: bool = True,
52
- database: str = None, query_id: int = None, stop_event=None):
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['query_str'] = sql
89
+ self.context["query_str"] = sql
86
90
  else:
87
91
  self.query = sql
88
- renderer = SqlalchemyRender('mysql')
92
+ renderer = SqlalchemyRender("mysql")
89
93
  try:
90
- self.context['query_str'] = renderer.get_string(self.query, with_failback=True)
94
+ self.context["query_str"] = renderer.get_string(self.query, with_failback=True)
91
95
  except Exception:
92
- self.context['query_str'] = str(self.query)
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['active'] = None
124
- args['version'] = table_version
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
- 'name': table_name,
136
- 'integration_name': project_name, # integration_name,
137
- 'timeseries': False,
138
- 'id': agent.id,
139
- 'to_predict': 'answer',
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 == 'error':
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'.{table_version}'
150
- and_version_str = f' and version = {table_version}'
149
+ dot_version_str = f".{table_version}"
150
+ and_version_str = f" and version = {table_version}"
151
151
 
152
- raise BadTableError(dedent(f'''\
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('timeseries_settings', {})
161
+ ts_settings = model_record.learn_args.get("timeseries_settings", {})
160
162
  predictor = {
161
- 'name': table_name,
162
- 'integration_name': project_name, # integration_name,
163
- 'timeseries': False,
164
- 'id': model_record.id,
165
- 'to_predict': model_record.to_predict,
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('is_timeseries') is True:
168
- window = ts_settings.get('window')
169
- order_by = ts_settings.get('order_by')
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('group_by')
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
- 'timeseries': True,
177
- 'window': window,
178
- 'horizon': ts_settings.get('horizon'),
179
- 'order_by_column': order_by,
180
- 'group_by_columns': group_by
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['model_types'] = model_record.data.get('dtypes', {})
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 == '' else self.database.lower()
191
+ database = None if self.database == "" else self.database.lower()
188
192
 
189
- self.context['predictor_metadata'] = predictor_metadata
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['columns']:
214
+ for col in statement_info["columns"]:
212
215
  self.columns_list.append(
213
216
  Column(
214
- database=col['ds'],
215
- table_name=col['table_name'],
216
- table_alias=col['table_alias'],
217
- name=col['name'],
218
- alias=col['alias'],
219
- type=col['type']
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(self.context['query_str'], database=self.database)
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('predict')
268
+ process_mark = create_process_mark("predict")
269
269
  for step in steps:
270
- with profiler.Context(f'step: {step.__class__.__name__}'):
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('predict', 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, 'columns_list') is False:
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('__mindsdb_row_id'):
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
- # fetch raw_query
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'Invalid url: {url}')
26
+ raise Exception(f"Invalid url: {url}")
27
27
  except Exception as e:
28
- raise Exception(f'URL parsing error: {e}')
29
- temp_dir = tempfile.mkdtemp(prefix='mindsdb_file_download_')
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('file')
35
- with open(str(temp_file_path), 'wb')as file:
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 format_db_error_message
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 Exception(
91
- format_db_error_message(db_type="DuckDB", db_error_msg=str(e), failed_query=query_str, is_external=False)
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 Exception("Only 'SELECT from TABLE' statements supported for internal query")
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 Exception(
218
- format_db_error_message(
219
- db_type="DuckDB",
220
- db_error_msg=(
221
- f"Unknown function: '{fnc_name}'. This function is not recognized during internal query processing.\n"
222
- "Please use DuckDB-supported functions instead."
223
- ),
224
- failed_query=query_str,
225
- is_external=False,
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 as e:
249
- logger.error(f"Exception during query casting to 'postgres' dialect. Query: {str(query)}. Error: {e}")
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 as e:
34
- logger.error(f"Error during downloading files from s3: {e}")
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
- str(destignation.joinpath("dist").joinpath("index.html")), static_folder
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)
@@ -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 as e:
158
- logger.error(f"Error in compatible-config.json structure: {e}")
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 as e:
353
- logger.error(f"Could not parse company id: {company_id} | exception: {e}")
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 as e:
360
- logger.error(f"Could not parse user_class: {user_class} | exception: {e}")
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 as e:
453
- logger.error(f"Failed to open {url} in webbrowser with exception {e}")
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()