MindsDB 25.9.2.0a1__py3-none-any.whl → 25.10.0rc1__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 (163) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +40 -29
  3. mindsdb/api/a2a/__init__.py +1 -1
  4. mindsdb/api/a2a/agent.py +16 -10
  5. mindsdb/api/a2a/common/server/server.py +7 -3
  6. mindsdb/api/a2a/common/server/task_manager.py +12 -5
  7. mindsdb/api/a2a/common/types.py +66 -0
  8. mindsdb/api/a2a/task_manager.py +65 -17
  9. mindsdb/api/common/middleware.py +10 -12
  10. mindsdb/api/executor/command_executor.py +51 -40
  11. mindsdb/api/executor/datahub/datanodes/datanode.py +2 -2
  12. mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +7 -13
  13. mindsdb/api/executor/datahub/datanodes/integration_datanode.py +101 -49
  14. mindsdb/api/executor/datahub/datanodes/project_datanode.py +8 -4
  15. mindsdb/api/executor/datahub/datanodes/system_tables.py +3 -2
  16. mindsdb/api/executor/exceptions.py +29 -10
  17. mindsdb/api/executor/planner/plan_join.py +17 -3
  18. mindsdb/api/executor/planner/query_prepare.py +2 -20
  19. mindsdb/api/executor/sql_query/sql_query.py +74 -74
  20. mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +1 -2
  21. mindsdb/api/executor/sql_query/steps/subselect_step.py +0 -1
  22. mindsdb/api/executor/utilities/functions.py +6 -6
  23. mindsdb/api/executor/utilities/sql.py +37 -20
  24. mindsdb/api/http/gui.py +5 -11
  25. mindsdb/api/http/initialize.py +75 -61
  26. mindsdb/api/http/namespaces/agents.py +10 -15
  27. mindsdb/api/http/namespaces/analysis.py +13 -20
  28. mindsdb/api/http/namespaces/auth.py +1 -1
  29. mindsdb/api/http/namespaces/chatbots.py +0 -5
  30. mindsdb/api/http/namespaces/config.py +15 -11
  31. mindsdb/api/http/namespaces/databases.py +140 -201
  32. mindsdb/api/http/namespaces/file.py +17 -4
  33. mindsdb/api/http/namespaces/handlers.py +17 -7
  34. mindsdb/api/http/namespaces/knowledge_bases.py +28 -7
  35. mindsdb/api/http/namespaces/models.py +94 -126
  36. mindsdb/api/http/namespaces/projects.py +13 -22
  37. mindsdb/api/http/namespaces/sql.py +33 -25
  38. mindsdb/api/http/namespaces/tab.py +27 -37
  39. mindsdb/api/http/namespaces/views.py +1 -1
  40. mindsdb/api/http/start.py +16 -10
  41. mindsdb/api/mcp/__init__.py +2 -1
  42. mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +15 -20
  43. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +26 -50
  44. mindsdb/api/mysql/mysql_proxy/utilities/__init__.py +0 -1
  45. mindsdb/api/mysql/mysql_proxy/utilities/dump.py +8 -2
  46. mindsdb/integrations/handlers/byom_handler/byom_handler.py +165 -190
  47. mindsdb/integrations/handlers/databricks_handler/databricks_handler.py +98 -46
  48. mindsdb/integrations/handlers/druid_handler/druid_handler.py +32 -40
  49. mindsdb/integrations/handlers/file_handler/file_handler.py +7 -0
  50. mindsdb/integrations/handlers/gitlab_handler/gitlab_handler.py +5 -2
  51. mindsdb/integrations/handlers/lightwood_handler/functions.py +45 -79
  52. mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +438 -100
  53. mindsdb/integrations/handlers/mssql_handler/requirements_odbc.txt +3 -0
  54. mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +235 -3
  55. mindsdb/integrations/handlers/oracle_handler/__init__.py +2 -0
  56. mindsdb/integrations/handlers/oracle_handler/connection_args.py +7 -1
  57. mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +321 -16
  58. mindsdb/integrations/handlers/oracle_handler/requirements.txt +1 -1
  59. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +14 -2
  60. mindsdb/integrations/handlers/shopify_handler/shopify_handler.py +25 -12
  61. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +2 -1
  62. mindsdb/integrations/handlers/statsforecast_handler/requirements.txt +1 -0
  63. mindsdb/integrations/handlers/statsforecast_handler/requirements_extra.txt +1 -0
  64. mindsdb/integrations/handlers/web_handler/urlcrawl_helpers.py +4 -4
  65. mindsdb/integrations/handlers/zendesk_handler/zendesk_tables.py +144 -111
  66. mindsdb/integrations/libs/api_handler.py +10 -10
  67. mindsdb/integrations/libs/base.py +4 -4
  68. mindsdb/integrations/libs/llm/utils.py +2 -2
  69. mindsdb/integrations/libs/ml_handler_process/create_engine_process.py +4 -7
  70. mindsdb/integrations/libs/ml_handler_process/func_call_process.py +2 -7
  71. mindsdb/integrations/libs/ml_handler_process/learn_process.py +37 -47
  72. mindsdb/integrations/libs/ml_handler_process/update_engine_process.py +4 -7
  73. mindsdb/integrations/libs/ml_handler_process/update_process.py +2 -7
  74. mindsdb/integrations/libs/process_cache.py +132 -140
  75. mindsdb/integrations/libs/response.py +18 -12
  76. mindsdb/integrations/libs/vectordatabase_handler.py +26 -0
  77. mindsdb/integrations/utilities/files/file_reader.py +6 -7
  78. mindsdb/integrations/utilities/handlers/auth_utilities/snowflake/__init__.py +1 -0
  79. mindsdb/integrations/utilities/handlers/auth_utilities/snowflake/snowflake_jwt_gen.py +151 -0
  80. mindsdb/integrations/utilities/rag/config_loader.py +37 -26
  81. mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +83 -30
  82. mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +4 -4
  83. mindsdb/integrations/utilities/rag/retrievers/sql_retriever.py +55 -133
  84. mindsdb/integrations/utilities/rag/settings.py +58 -133
  85. mindsdb/integrations/utilities/rag/splitters/file_splitter.py +5 -15
  86. mindsdb/interfaces/agents/agents_controller.py +2 -3
  87. mindsdb/interfaces/agents/constants.py +0 -2
  88. mindsdb/interfaces/agents/litellm_server.py +34 -58
  89. mindsdb/interfaces/agents/mcp_client_agent.py +10 -10
  90. mindsdb/interfaces/agents/mindsdb_database_agent.py +5 -5
  91. mindsdb/interfaces/agents/run_mcp_agent.py +12 -21
  92. mindsdb/interfaces/chatbot/chatbot_task.py +20 -23
  93. mindsdb/interfaces/chatbot/polling.py +30 -18
  94. mindsdb/interfaces/data_catalog/data_catalog_loader.py +16 -17
  95. mindsdb/interfaces/data_catalog/data_catalog_reader.py +15 -4
  96. mindsdb/interfaces/database/data_handlers_cache.py +190 -0
  97. mindsdb/interfaces/database/database.py +3 -3
  98. mindsdb/interfaces/database/integrations.py +7 -110
  99. mindsdb/interfaces/database/projects.py +2 -6
  100. mindsdb/interfaces/database/views.py +1 -4
  101. mindsdb/interfaces/file/file_controller.py +6 -6
  102. mindsdb/interfaces/functions/controller.py +1 -1
  103. mindsdb/interfaces/functions/to_markdown.py +2 -2
  104. mindsdb/interfaces/jobs/jobs_controller.py +5 -9
  105. mindsdb/interfaces/jobs/scheduler.py +3 -9
  106. mindsdb/interfaces/knowledge_base/controller.py +244 -128
  107. mindsdb/interfaces/knowledge_base/evaluate.py +36 -41
  108. mindsdb/interfaces/knowledge_base/executor.py +11 -0
  109. mindsdb/interfaces/knowledge_base/llm_client.py +51 -17
  110. mindsdb/interfaces/knowledge_base/preprocessing/json_chunker.py +40 -61
  111. mindsdb/interfaces/model/model_controller.py +172 -168
  112. mindsdb/interfaces/query_context/context_controller.py +14 -2
  113. mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +10 -14
  114. mindsdb/interfaces/skills/retrieval_tool.py +43 -50
  115. mindsdb/interfaces/skills/skill_tool.py +2 -2
  116. mindsdb/interfaces/skills/skills_controller.py +1 -4
  117. mindsdb/interfaces/skills/sql_agent.py +25 -19
  118. mindsdb/interfaces/storage/db.py +16 -6
  119. mindsdb/interfaces/storage/fs.py +114 -169
  120. mindsdb/interfaces/storage/json.py +19 -18
  121. mindsdb/interfaces/tabs/tabs_controller.py +49 -72
  122. mindsdb/interfaces/tasks/task_monitor.py +3 -9
  123. mindsdb/interfaces/tasks/task_thread.py +7 -9
  124. mindsdb/interfaces/triggers/trigger_task.py +7 -13
  125. mindsdb/interfaces/triggers/triggers_controller.py +47 -52
  126. mindsdb/migrations/migrate.py +16 -16
  127. mindsdb/utilities/api_status.py +58 -0
  128. mindsdb/utilities/config.py +68 -2
  129. mindsdb/utilities/exception.py +40 -1
  130. mindsdb/utilities/fs.py +0 -1
  131. mindsdb/utilities/hooks/profiling.py +17 -14
  132. mindsdb/utilities/json_encoder.py +24 -10
  133. mindsdb/utilities/langfuse.py +40 -45
  134. mindsdb/utilities/log.py +272 -0
  135. mindsdb/utilities/ml_task_queue/consumer.py +52 -58
  136. mindsdb/utilities/ml_task_queue/producer.py +26 -30
  137. mindsdb/utilities/render/sqlalchemy_render.py +22 -20
  138. mindsdb/utilities/starters.py +0 -10
  139. mindsdb/utilities/utils.py +2 -2
  140. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/METADATA +293 -276
  141. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/RECORD +144 -158
  142. mindsdb/api/mysql/mysql_proxy/utilities/exceptions.py +0 -14
  143. mindsdb/api/postgres/__init__.py +0 -0
  144. mindsdb/api/postgres/postgres_proxy/__init__.py +0 -0
  145. mindsdb/api/postgres/postgres_proxy/executor/__init__.py +0 -1
  146. mindsdb/api/postgres/postgres_proxy/executor/executor.py +0 -189
  147. mindsdb/api/postgres/postgres_proxy/postgres_packets/__init__.py +0 -0
  148. mindsdb/api/postgres/postgres_proxy/postgres_packets/errors.py +0 -322
  149. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_fields.py +0 -34
  150. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message.py +0 -31
  151. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_formats.py +0 -1265
  152. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_identifiers.py +0 -31
  153. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_packets.py +0 -253
  154. mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +0 -477
  155. mindsdb/api/postgres/postgres_proxy/utilities/__init__.py +0 -10
  156. mindsdb/api/postgres/start.py +0 -11
  157. mindsdb/integrations/handlers/mssql_handler/tests/__init__.py +0 -0
  158. mindsdb/integrations/handlers/mssql_handler/tests/test_mssql_handler.py +0 -169
  159. mindsdb/integrations/handlers/oracle_handler/tests/__init__.py +0 -0
  160. mindsdb/integrations/handlers/oracle_handler/tests/test_oracle_handler.py +0 -32
  161. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/WHEEL +0 -0
  162. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/licenses/LICENSE +0 -0
  163. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/top_level.txt +0 -0
@@ -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 = []
@@ -286,26 +286,8 @@ class PreparedStatementPlanner:
286
286
 
287
287
  if step.result_data is not None:
288
288
  # save results
289
-
290
- if len(step.result_data["tables"]) > 0:
291
- table_info = step.result_data["tables"][0]
292
- columns_info = step.result_data["columns"][table_info]
293
-
294
- table.columns = []
295
- table.ds = table_info[0]
296
- for col in columns_info:
297
- if isinstance(col, tuple):
298
- # is predictor
299
- col = dict(name=col[0], type="str")
300
- table.columns.append(
301
- Column(
302
- name=col["name"],
303
- type=col["type"],
304
- )
305
- )
306
-
307
- # map by names
308
- table.columns_map = {i.name.upper(): i for i in table.columns}
289
+ table.columns = step.result_data.columns
290
+ table.columns_map = {column.name.upper(): column for column in step.result_data.columns}
309
291
 
310
292
  # === create columns list ===
311
293
  columns_result = []
@@ -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)
@@ -4,13 +4,14 @@ from typing import List
4
4
  import duckdb
5
5
  from duckdb import InvalidInputException
6
6
  import numpy as np
7
+ import orjson
7
8
 
8
9
  from mindsdb_sql_parser import parse_sql
9
10
  from mindsdb_sql_parser.ast import ASTNode, Select, Identifier, Function, Constant
10
11
 
11
12
  from mindsdb.integrations.utilities.query_traversal import query_traversal
12
13
  from mindsdb.utilities import log
13
- from mindsdb.utilities.exception import format_db_error_message
14
+ from mindsdb.utilities.exception import QueryError
14
15
  from mindsdb.utilities.functions import resolve_table_identifier, resolve_model_identifier
15
16
  from mindsdb.utilities.json_encoder import CustomJSONEncoder
16
17
  from mindsdb.utilities.render.sqlalchemy_render import SqlalchemyRender
@@ -64,6 +65,9 @@ def query_df_with_type_infer_fallback(query_str: str, dataframes: dict, user_fun
64
65
  Returns:
65
66
  pandas.DataFrame
66
67
  pandas.columns
68
+
69
+ Raises:
70
+ QueryError: Raised when DuckDB fails to execute the query
67
71
  """
68
72
 
69
73
  try:
@@ -86,9 +90,17 @@ def query_df_with_type_infer_fallback(query_str: str, dataframes: dict, user_fun
86
90
  else:
87
91
  raise exception
88
92
  description = con.description
93
+ except InvalidInputException as e:
94
+ raise QueryError(
95
+ db_type="DuckDB",
96
+ db_error_msg=f"DuckDB failed to execute query, likely due to inability to determine column data types. Details: {e}",
97
+ failed_query=query_str,
98
+ is_external=False,
99
+ is_expected=False,
100
+ ) from e
89
101
  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)
102
+ raise QueryError(
103
+ db_type="DuckDB", db_error_msg=str(e), failed_query=query_str, is_external=False, is_expected=False
92
104
  ) from e
93
105
 
94
106
  return result_df, description
@@ -164,7 +176,13 @@ def query_df(df, query, session=None):
164
176
  query_str = str(query)
165
177
 
166
178
  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")
179
+ raise QueryError(
180
+ db_type="DuckDB",
181
+ db_error_msg="Only 'SELECT from TABLE' statements supported for internal query",
182
+ failed_query=query_str,
183
+ is_external=False,
184
+ is_expected=False,
185
+ )
168
186
 
169
187
  table_name = query_ast.from_table.parts[0]
170
188
  query_ast.from_table.parts = ["df"]
@@ -214,27 +232,26 @@ def query_df(df, query, session=None):
214
232
  custom_functions_list = [] if user_functions is None else list(user_functions.functions.keys())
215
233
  all_functions_list = duckdb_functions_and_kw_list + custom_functions_list
216
234
  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
- )
235
+ raise QueryError(
236
+ db_type="DuckDB",
237
+ db_error_msg=(
238
+ f"Unknown function: '{fnc_name}'. This function is not recognized during internal query processing.\n"
239
+ "Please use DuckDB-supported functions instead."
240
+ ),
241
+ failed_query=query_str,
242
+ is_external=False,
243
+ is_expected=False,
227
244
  )
228
245
 
229
246
  query_traversal(query_ast, adapt_query)
230
247
 
231
- # convert json columns
232
- encoder = CustomJSONEncoder()
233
-
234
248
  def _convert(v):
235
249
  if isinstance(v, dict) or isinstance(v, list):
236
250
  try:
237
- return encoder.encode(v)
251
+ default_encoder = CustomJSONEncoder().default
252
+ return orjson.dumps(
253
+ v, default=default_encoder, option=orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_PASSTHROUGH_DATETIME
254
+ ).decode("utf-8")
238
255
  except Exception:
239
256
  pass
240
257
  return v
@@ -245,8 +262,8 @@ def query_df(df, query, session=None):
245
262
  render = SqlalchemyRender("postgres")
246
263
  try:
247
264
  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}")
265
+ except Exception:
266
+ logger.exception(f"Exception during query casting to 'postgres' dialect. Query:\n{str(query)}.\nError:")
250
267
  query_str = render.get_string(query_ast, with_failback=True)
251
268
 
252
269
  # 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)