MindsDB 25.3.4.2__py3-none-any.whl → 25.4.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of MindsDB might be problematic. Click here for more details.

Files changed (53) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +21 -4
  3. mindsdb/api/executor/command_executor.py +62 -61
  4. mindsdb/api/executor/data_types/answer.py +9 -12
  5. mindsdb/api/executor/datahub/classes/response.py +11 -0
  6. mindsdb/api/executor/datahub/datanodes/datanode.py +4 -4
  7. mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +7 -9
  8. mindsdb/api/executor/datahub/datanodes/integration_datanode.py +22 -16
  9. mindsdb/api/executor/datahub/datanodes/project_datanode.py +20 -20
  10. mindsdb/api/executor/planner/plan_join.py +1 -1
  11. mindsdb/api/executor/planner/steps.py +2 -1
  12. mindsdb/api/executor/sql_query/result_set.py +10 -7
  13. mindsdb/api/executor/sql_query/sql_query.py +36 -82
  14. mindsdb/api/executor/sql_query/steps/delete_step.py +2 -3
  15. mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +5 -3
  16. mindsdb/api/executor/sql_query/steps/insert_step.py +2 -2
  17. mindsdb/api/executor/sql_query/steps/prepare_steps.py +2 -2
  18. mindsdb/api/executor/sql_query/steps/subselect_step.py +20 -8
  19. mindsdb/api/executor/sql_query/steps/update_step.py +4 -6
  20. mindsdb/api/http/namespaces/sql.py +4 -1
  21. mindsdb/api/mcp/__init__.py +0 -0
  22. mindsdb/api/mcp/start.py +152 -0
  23. mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/ok_packet.py +1 -1
  24. mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +4 -27
  25. mindsdb/api/mysql/mysql_proxy/libs/constants/mysql.py +1 -0
  26. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +38 -37
  27. mindsdb/integrations/handlers/chromadb_handler/chromadb_handler.py +23 -13
  28. mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +1 -1
  29. mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +3 -2
  30. mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +4 -4
  31. mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +19 -5
  32. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +9 -4
  33. mindsdb/integrations/handlers/redshift_handler/redshift_handler.py +1 -1
  34. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +18 -11
  35. mindsdb/integrations/libs/ml_handler_process/learn_process.py +1 -2
  36. mindsdb/integrations/libs/response.py +9 -4
  37. mindsdb/integrations/libs/vectordatabase_handler.py +37 -25
  38. mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +35 -15
  39. mindsdb/interfaces/database/log.py +8 -9
  40. mindsdb/interfaces/database/projects.py +16 -5
  41. mindsdb/interfaces/functions/controller.py +59 -17
  42. mindsdb/interfaces/functions/to_markdown.py +194 -0
  43. mindsdb/interfaces/jobs/jobs_controller.py +3 -3
  44. mindsdb/interfaces/knowledge_base/controller.py +143 -26
  45. mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +3 -14
  46. mindsdb/interfaces/query_context/context_controller.py +3 -1
  47. mindsdb/utilities/config.py +8 -0
  48. mindsdb/utilities/starters.py +7 -0
  49. {mindsdb-25.3.4.2.dist-info → mindsdb-25.4.2.0.dist-info}/METADATA +233 -231
  50. {mindsdb-25.3.4.2.dist-info → mindsdb-25.4.2.0.dist-info}/RECORD +53 -49
  51. {mindsdb-25.3.4.2.dist-info → mindsdb-25.4.2.0.dist-info}/WHEEL +0 -0
  52. {mindsdb-25.3.4.2.dist-info → mindsdb-25.4.2.0.dist-info}/licenses/LICENSE +0 -0
  53. {mindsdb-25.3.4.2.dist-info → mindsdb-25.4.2.0.dist-info}/top_level.txt +0 -0
@@ -249,11 +249,12 @@ class SubSelectStep(PlanStep):
249
249
 
250
250
 
251
251
  class QueryStep(PlanStep):
252
- def __init__(self, query, from_table=None, *args, **kwargs):
252
+ def __init__(self, query, from_table=None, *args, strict_where=True, **kwargs):
253
253
  """Performs query using injected dataframe"""
254
254
  super().__init__(*args, **kwargs)
255
255
  self.query = query
256
256
  self.from_table = from_table
257
+ self.strict_where = strict_where
257
258
 
258
259
 
259
260
  class DataStep(PlanStep):
@@ -50,13 +50,14 @@ def rename_df_columns(df: pd.DataFrame, names: Optional[List] = None) -> None:
50
50
 
51
51
 
52
52
  class ResultSet:
53
- def __init__(self, columns=None, values: List[List] = None, df: pd.DataFrame = None):
54
- '''
55
-
56
- :param columns: list of Columns
57
- :param values: data of resultSet, have to be list of lists with length equal to column
58
- :param df: injected dataframe, have to have enumerated columns and length equal to columns
59
- '''
53
+ def __init__(self, columns=None, values: List[List] = None, df: pd.DataFrame = None, affected_rows: int = None):
54
+ """
55
+ Args:
56
+ columns: list of Columns
57
+ values (List[List]): data of resultSet, have to be list of lists with length equal to column
58
+ df (pd.DataFrame): injected dataframe, have to have enumerated columns and length equal to columns
59
+ affected_rows (int): number of affected rows
60
+ """
60
61
  if columns is None:
61
62
  columns = []
62
63
  self._columns = columns
@@ -67,6 +68,8 @@ class ResultSet:
67
68
  df = pd.DataFrame(values)
68
69
  self._df = df
69
70
 
71
+ self.affected_rows = affected_rows
72
+
70
73
  self.is_prediction = False
71
74
 
72
75
  def __repr__(self):
@@ -8,9 +8,9 @@
8
8
  * permission of MindsDB Inc
9
9
  *******************************************************
10
10
  """
11
- import re
12
11
  import inspect
13
12
  from textwrap import dedent
13
+ from typing import Dict
14
14
 
15
15
  from mindsdb_sql_parser import parse_sql
16
16
  from mindsdb.api.executor.planner.steps import (
@@ -23,7 +23,7 @@ from mindsdb.api.executor.planner.exceptions import PlanningException
23
23
  from mindsdb.utilities.render.sqlalchemy_render import SqlalchemyRender
24
24
  from mindsdb.api.executor.planner import query_planner
25
25
 
26
- from mindsdb.api.executor.utilities.sql import query_df, get_query_models
26
+ from mindsdb.api.executor.utilities.sql import get_query_models
27
27
  from mindsdb.interfaces.model.functions import get_model_record
28
28
  from mindsdb.api.executor.exceptions import (
29
29
  BadTableError,
@@ -38,8 +38,6 @@ from . import steps
38
38
  from .result_set import ResultSet, Column
39
39
  from . steps.base import BaseStepCall
40
40
 
41
- superset_subquery = re.compile(r'from[\s\n]*(\(.*\))[\s\n]*as[\s\n]*virtual_table', flags=re.IGNORECASE | re.MULTILINE | re.S)
42
-
43
41
 
44
42
  class SQLQuery:
45
43
 
@@ -59,23 +57,13 @@ class SQLQuery:
59
57
  }
60
58
 
61
59
  self.columns_list = None
62
- self.steps_data = {}
60
+ self.steps_data: Dict[int, ResultSet] = {}
63
61
 
64
- self.planner = None
62
+ self.planner: query_planner.QueryPlanner = None
65
63
  self.parameters = []
66
- self.fetched_data = None
67
-
68
- self.outer_query = None
64
+ self.fetched_data: ResultSet = None
69
65
 
70
66
  if isinstance(sql, str):
71
- # region workaround for subqueries in superset
72
- if 'as virtual_table' in sql.lower():
73
- subquery = re.findall(superset_subquery, sql)
74
- if isinstance(subquery, list) and len(subquery) == 1:
75
- subquery = subquery[0]
76
- self.outer_query = sql.replace(subquery, 'dataframe')
77
- sql = subquery.strip('()')
78
- # endregion
79
67
  self.query = parse_sql(sql)
80
68
  self.context['query_str'] = sql
81
69
  else:
@@ -89,7 +77,6 @@ class SQLQuery:
89
77
  self.create_planner()
90
78
 
91
79
  if execute:
92
- self.prepare_query(prepare=False)
93
80
  self.execute_query()
94
81
 
95
82
  @classmethod
@@ -190,55 +177,42 @@ class SQLQuery:
190
177
  default_namespace=database,
191
178
  )
192
179
 
193
- def fetch(self, view='result_set'):
194
- data = self.fetched_data
195
-
196
- if view == 'dataframe':
197
- result = data.to_df()
198
- else:
199
- result = data
180
+ def prepare_query(self):
181
+ """it is prepared statement call
182
+ """
183
+ try:
184
+ for step in self.planner.prepare_steps(self.query):
185
+ data = self.execute_step(step)
186
+ step.set_result(data)
187
+ self.steps_data[step.step_num] = data
188
+ except PlanningException as e:
189
+ raise LogicError(e)
200
190
 
201
- return {
202
- 'success': True,
203
- 'result': result
204
- }
191
+ statement_info = self.planner.get_statement_info()
205
192
 
206
- def prepare_query(self, prepare=True):
207
- if prepare:
208
- # it is prepared statement call
209
- try:
210
- for step in self.planner.prepare_steps(self.query):
211
- data = self.execute_step(step)
212
- step.set_result(data)
213
- self.steps_data[step.step_num] = data
214
- except PlanningException as e:
215
- raise LogicError(e)
216
-
217
- statement_info = self.planner.get_statement_info()
218
-
219
- self.columns_list = []
220
- for col in statement_info['columns']:
221
- self.columns_list.append(
222
- Column(
223
- database=col['ds'],
224
- table_name=col['table_name'],
225
- table_alias=col['table_alias'],
226
- name=col['name'],
227
- alias=col['alias'],
228
- type=col['type']
229
- )
230
- )
231
-
232
- self.parameters = [
193
+ self.columns_list = []
194
+ for col in statement_info['columns']:
195
+ self.columns_list.append(
233
196
  Column(
197
+ database=col['ds'],
198
+ table_name=col['table_name'],
199
+ table_alias=col['table_alias'],
234
200
  name=col['name'],
235
201
  alias=col['alias'],
236
202
  type=col['type']
237
203
  )
238
- for col in statement_info['parameters']
239
- ]
240
-
241
- def execute_query(self, params=None):
204
+ )
205
+
206
+ self.parameters = [
207
+ Column(
208
+ name=col['name'],
209
+ alias=col['alias'],
210
+ type=col['type']
211
+ )
212
+ for col in statement_info['parameters']
213
+ ]
214
+
215
+ def execute_query(self):
242
216
  if self.fetched_data is not None:
243
217
  # no need to execute
244
218
  return
@@ -246,7 +220,7 @@ class SQLQuery:
246
220
  step_result = None
247
221
  process_mark = None
248
222
  try:
249
- steps = list(self.planner.execute_steps(params))
223
+ steps = list(self.planner.execute_steps())
250
224
  steps_classes = (x.__class__ for x in steps)
251
225
  predict_steps = (ApplyPredictorRowStep, ApplyPredictorStep, ApplyTimeseriesPredictorStep)
252
226
  if any(s in predict_steps for s in steps_classes):
@@ -270,27 +244,7 @@ class SQLQuery:
270
244
  if len(self.steps_data) == 0:
271
245
  return
272
246
 
273
- try:
274
- if self.outer_query is not None:
275
- # workaround for subqueries in superset. remove it?
276
- # +++
277
- # ???
278
-
279
- result = step_result
280
- df = result.to_df()
281
-
282
- df2 = query_df(df, self.outer_query)
283
-
284
- result2 = ResultSet().from_df(df2, database='', table_name='')
285
-
286
- self.columns_list = result2.columns
287
- self.fetched_data = result2
288
-
289
- else:
290
- result = step_result
291
- self.fetched_data = result
292
- except Exception as e:
293
- raise UnknownError("error in preparing result query step") from e
247
+ self.fetched_data = step_result
294
248
 
295
249
  try:
296
250
  if hasattr(self, 'columns_list') is False:
@@ -44,6 +44,5 @@ class DeleteStepCall(BaseStepCall):
44
44
 
45
45
  query_traversal(query.where, fill_params)
46
46
 
47
- dn.query(query=query, session=self.session)
48
-
49
- return ResultSet()
47
+ response = dn.query(query=query, session=self.session)
48
+ return ResultSet(affected_rows=response.affected_rows)
@@ -89,10 +89,11 @@ class FetchDataframeStepCall(BaseStepCall):
89
89
  table_alias = (self.context.get('database'), 'result', 'result')
90
90
 
91
91
  # fetch raw_query
92
- df, columns_info = dn.query(
92
+ response = dn.query(
93
93
  native_query=step.raw_query,
94
94
  session=self.session
95
95
  )
96
+ df = response.data_frame
96
97
  else:
97
98
  table_alias = get_table_alias(step.query.from_table, self.context.get('database'))
98
99
 
@@ -104,13 +105,14 @@ class FetchDataframeStepCall(BaseStepCall):
104
105
 
105
106
  query, context_callback = query_context_controller.handle_db_context_vars(query, dn, self.session)
106
107
 
107
- df, columns_info = dn.query(
108
+ response = dn.query(
108
109
  query=query,
109
110
  session=self.session
110
111
  )
112
+ df = response.data_frame
111
113
 
112
114
  if context_callback:
113
- context_callback(df, columns_info)
115
+ context_callback(df, response.columns)
114
116
 
115
117
  result = ResultSet()
116
118
 
@@ -91,13 +91,13 @@ class InsertToTableCall(BaseStepCall):
91
91
  else:
92
92
  col_names.add(col.alias)
93
93
 
94
- dn.create_table(
94
+ response = dn.create_table(
95
95
  table_name=table_name,
96
96
  result_set=data,
97
97
  is_replace=is_replace,
98
98
  is_create=is_create
99
99
  )
100
- return ResultSet()
100
+ return ResultSet(affected_rows=response.affected_rows)
101
101
 
102
102
 
103
103
  class SaveToTableCall(InsertToTableCall):
@@ -47,10 +47,10 @@ class GetTableColumnsCall(BaseStepCall):
47
47
  dn = self.session.datahub.get(step.namespace)
48
48
  ds_query = Select(from_table=Identifier(table), targets=[Star()], limit=Constant(0))
49
49
 
50
- data, columns_info = dn.query(ds_query, session=self.session)
50
+ response = dn.query(ds_query, session=self.session)
51
51
 
52
52
  data = ResultSet()
53
- for column in columns_info:
53
+ for column in response.columns:
54
54
  data.add_column(Column(
55
55
  name=column['name'],
56
56
  type=column.get('type'),
@@ -3,13 +3,7 @@ from collections import defaultdict
3
3
  import pandas as pd
4
4
 
5
5
  from mindsdb_sql_parser.ast import (
6
- Identifier,
7
- Select,
8
- Star,
9
- Constant,
10
- Parameter,
11
- Function,
12
- Variable
6
+ Identifier, Select, Star, Constant, Parameter, Function, Variable, BinaryOperation
13
7
  )
14
8
 
15
9
  from mindsdb.api.mysql.mysql_proxy.libs.constants.mysql import SERVER_VARIABLES
@@ -87,7 +81,7 @@ class QueryStepCall(BaseStepCall):
87
81
 
88
82
  bind = QueryStep
89
83
 
90
- def call(self, step):
84
+ def call(self, step: QueryStep):
91
85
  query = step.query
92
86
 
93
87
  if step.from_table is not None:
@@ -190,6 +184,24 @@ class QueryStepCall(BaseStepCall):
190
184
  fill_params = get_fill_param_fnc(self.steps_data)
191
185
  query_traversal(query, fill_params)
192
186
 
187
+ if not step.strict_where:
188
+ # remove conditions with not-existed columns.
189
+ # these conditions can be already used as input to model or knowledge base
190
+ # but can be absent in their output
191
+
192
+ def remove_not_used_conditions(node, **kwargs):
193
+ # find last in where
194
+ if isinstance(node, BinaryOperation):
195
+ for arg in node.args:
196
+ if isinstance(arg, Identifier) and len(arg.parts) > 1:
197
+ key = tuple(arg.parts[-2:])
198
+ if key not in col_idx:
199
+ # exclude
200
+ node.args = [Constant(0), Constant(0)]
201
+ node.op = '='
202
+
203
+ query_traversal(query.where, remove_not_used_conditions)
204
+
193
205
  query_traversal(query, check_fields)
194
206
  query.where = query_context_controller.remove_lasts(query.where)
195
207
 
@@ -18,8 +18,6 @@ class UpdateToTableCall(BaseStepCall):
18
18
  bind = UpdateToTable
19
19
 
20
20
  def call(self, step):
21
- data = ResultSet()
22
-
23
21
  if len(step.table.parts) > 1:
24
22
  integration_name = step.table.parts[0]
25
23
  table_name_parts = step.table.parts[1:]
@@ -85,8 +83,8 @@ class UpdateToTableCall(BaseStepCall):
85
83
 
86
84
  if result_step is None:
87
85
  # run as is
88
- dn.query(query=update_query, session=self.session)
89
- return data
86
+ response = dn.query(query=update_query, session=self.session)
87
+ return ResultSet(affected_rows=response.affected_rows)
90
88
  result_data = self.steps_data[result_step.result.step_num]
91
89
 
92
90
  # link nodes with parameters for fast replacing with values
@@ -125,5 +123,5 @@ class UpdateToTableCall(BaseStepCall):
125
123
  for param_name, param in params_map_index:
126
124
  param.value = row[param_name]
127
125
 
128
- dn.query(query=update_query, session=self.session)
129
- return data
126
+ response = dn.query(query=update_query, session=self.session)
127
+ return ResultSet(affected_rows=response.affected_rows)
@@ -59,7 +59,10 @@ class Query(Resource):
59
59
  result = mysql_proxy.process_query(query)
60
60
 
61
61
  if result.type == SQL_RESPONSE_TYPE.OK:
62
- query_response = {"type": SQL_RESPONSE_TYPE.OK}
62
+ query_response = {
63
+ "type": SQL_RESPONSE_TYPE.OK,
64
+ "affected_rows": result.affected_rows
65
+ }
63
66
  elif result.type == SQL_RESPONSE_TYPE.TABLE:
64
67
  data = result.data.to_lists(json_types=True)
65
68
  query_response = {
File without changes
@@ -0,0 +1,152 @@
1
+ from contextlib import asynccontextmanager
2
+ from collections.abc import AsyncIterator
3
+ from typing import Optional, Dict, Any
4
+ from dataclasses import dataclass
5
+
6
+ from mcp.server.fastmcp import FastMCP
7
+ from mindsdb.api.mysql.mysql_proxy.classes.fake_mysql_proxy import FakeMysqlProxy
8
+ from mindsdb.api.executor.data_types.response_type import RESPONSE_TYPE as SQL_RESPONSE_TYPE
9
+ from mindsdb.utilities import log
10
+ from mindsdb.utilities.config import Config
11
+ from mindsdb.interfaces.storage import db
12
+
13
+ logger = log.getLogger(__name__)
14
+
15
+
16
+ @dataclass
17
+ class AppContext:
18
+ db: Any
19
+
20
+
21
+ @asynccontextmanager
22
+ async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
23
+ """Manage application lifecycle with type-safe context"""
24
+ # Initialize on startup
25
+ db.init()
26
+ try:
27
+ yield AppContext(db=db)
28
+ finally:
29
+ # TODO: We need better way to handle this in storage/db.py
30
+ pass
31
+
32
+
33
+ # Configure server with lifespan
34
+ mcp = FastMCP(
35
+ "MindsDB",
36
+ lifespan=app_lifespan,
37
+ dependencies=["mindsdb"] # Add any additional dependencies
38
+ )
39
+ # MCP Queries
40
+ LISTING_QUERY = "SHOW DATABASES"
41
+
42
+
43
+ @mcp.tool()
44
+ def query(query: str, context: Optional[Dict] = None) -> Dict[str, Any]:
45
+ """
46
+ Execute a SQL query against MindsDB
47
+
48
+ Args:
49
+ query: The SQL query to execute
50
+ context: Optional context parameters for the query
51
+
52
+ Returns:
53
+ Dict containing the query results or error information
54
+ """
55
+
56
+ if context is None:
57
+ context = {}
58
+
59
+ logger.debug(f'Incoming MCP query: {query}')
60
+
61
+ mysql_proxy = FakeMysqlProxy()
62
+ mysql_proxy.set_context(context)
63
+
64
+ try:
65
+ result = mysql_proxy.process_query(query)
66
+
67
+ if result.type == SQL_RESPONSE_TYPE.OK:
68
+ return {"type": SQL_RESPONSE_TYPE.OK}
69
+
70
+ if result.type == SQL_RESPONSE_TYPE.TABLE:
71
+ return {
72
+ "type": SQL_RESPONSE_TYPE.TABLE,
73
+ "data": result.data.to_lists(json_types=True),
74
+ "column_names": [
75
+ x["alias"] or x["name"] if "alias" in x else x["name"]
76
+ for x in result.columns
77
+ ],
78
+ }
79
+ else:
80
+ return {
81
+ "type": SQL_RESPONSE_TYPE.ERROR,
82
+ "error_code": 0,
83
+ "error_message": "Unknown response type"
84
+ }
85
+
86
+ except Exception as e:
87
+ logger.error(f"Error processing query: {str(e)}")
88
+ return {
89
+ "type": SQL_RESPONSE_TYPE.ERROR,
90
+ "error_code": 0,
91
+ "error_message": str(e)
92
+ }
93
+
94
+
95
+ @mcp.tool()
96
+ def list_databases() -> Dict[str, Any]:
97
+ """
98
+ List all databases in MindsDB along with their tables
99
+
100
+ Returns:
101
+ Dict containing the list of databases and their tables
102
+ """
103
+
104
+ mysql_proxy = FakeMysqlProxy()
105
+
106
+ try:
107
+ result = mysql_proxy.process_query(LISTING_QUERY)
108
+ if result.type == SQL_RESPONSE_TYPE.ERROR:
109
+ return {
110
+ "type": "error",
111
+ "error_code": result.error_code,
112
+ "error_message": result.error_message,
113
+ }
114
+
115
+ elif result.type == SQL_RESPONSE_TYPE.OK:
116
+ return {"type": "ok"}
117
+
118
+ elif result.type == SQL_RESPONSE_TYPE.TABLE:
119
+ data = result.data.to_lists(json_types=True)
120
+ return data
121
+
122
+ except Exception as e:
123
+ return {
124
+ "type": "error",
125
+ "error_code": 0,
126
+ "error_message": str(e),
127
+ }
128
+
129
+
130
+ def start(*args, **kwargs):
131
+ """Start the MCP server
132
+ Args:
133
+ host (str): Host to bind to
134
+ port (int): Port to listen on
135
+ """
136
+ config = Config()
137
+ port = int(config['api'].get('mcp', {}).get('port', 47337))
138
+ host = config['api'].get('mcp', {}).get('host', '127.0.0.1')
139
+
140
+ logger.info(f"Starting MCP server on {host}:{port}")
141
+ mcp.settings.host = host
142
+ mcp.settings.port = port
143
+
144
+ try:
145
+ mcp.run(transport="sse") # Use SSE transport instead of stdio
146
+ except Exception as e:
147
+ logger.error(f"Error starting MCP server: {str(e)}")
148
+ raise
149
+
150
+
151
+ if __name__ == "__main__":
152
+ start()
@@ -40,7 +40,7 @@ class OkPacket(Packet):
40
40
  def setup(self):
41
41
  eof = self._kwargs.get('eof', False)
42
42
  self.ok_header = Datum('int<1>', 0xFE if eof is True else 0)
43
- self.affected_rows = Datum('int<lenenc>', self._kwargs.get('affected_rows', 0))
43
+ self.affected_rows = Datum('int<lenenc>', self._kwargs.get('affected_rows') or 0)
44
44
  self.last_insert_id = Datum('int<lenenc>', 0)
45
45
  status = self._kwargs.get('status', 0x0002)
46
46
  self.server_status = Datum('int<2>', status)
@@ -4,6 +4,7 @@ from mindsdb.api.executor.planner import utils as planner_utils
4
4
  import mindsdb.utilities.profiler as profiler
5
5
  from mindsdb.api.executor.sql_query.result_set import Column
6
6
  from mindsdb.api.executor.sql_query import SQLQuery
7
+ from mindsdb.api.executor.data_types.answer import ExecuteAnswer
7
8
  from mindsdb.api.executor.command_executor import ExecuteCommands
8
9
  from mindsdb.api.mysql.mysql_proxy.utilities import ErSqlSyntaxError
9
10
  from mindsdb.utilities import log
@@ -12,37 +13,20 @@ logger = log.getLogger(__name__)
12
13
 
13
14
 
14
15
  class Executor:
15
- """This class stores initial and intermediate params
16
- between different steps of query execution. And it is also
17
- creates a separate instance of ExecuteCommands to execute the current
18
- query step.
19
-
20
- IMPORTANT: A public API of this class is a contract.
21
- And there are at least 2 classes strongly depend on it:
22
- ExecuctorClient
23
- ExecutorService.
24
- These classes do the same work as Executor when
25
- MindsDB works in 'modularity' mode.
26
- Thus please make sure that IF you change the API,
27
- you must update the API of these two classes as well!"""
28
-
29
16
  def __init__(self, session, sqlserver):
30
17
  self.session = session
31
18
  self.sqlserver = sqlserver
32
19
 
33
20
  self.query = None
34
21
 
35
- # returned values
36
- # all this attributes needs to be added in
37
- # self.json() method
38
22
  self.columns = []
39
23
  self.params = []
40
24
  self.data = None
41
- self.state_track = None
42
25
  self.server_status = None
43
26
  self.is_executed = False
44
27
  self.error_message = None
45
28
  self.error_code = None
29
+ self.executor_answer: ExecuteAnswer = None
46
30
 
47
31
  self.sql = ""
48
32
  self.sql_lower = ""
@@ -126,14 +110,7 @@ class Executor:
126
110
  if self.is_executed:
127
111
  return
128
112
 
129
- ret = self.command_executor.execute_command(self.query)
130
- self.error_code = ret.error_code
131
- self.error_message = ret.error_message
113
+ executor_answer: ExecuteAnswer = self.command_executor.execute_command(self.query)
114
+ self.executor_answer = executor_answer
132
115
 
133
116
  self.is_executed = True
134
-
135
- if ret.data is not None:
136
- self.data = ret.data
137
- self.columns = ret.data.columns
138
-
139
- self.state_track = ret.state_track
@@ -94,6 +94,7 @@ class COMMANDS(object):
94
94
  COM_STMT_PREPARE = int('0x16', 0)
95
95
  COM_STMT_EXECUTE = int('0x17', 0)
96
96
  COM_STMT_FETCH = int('0x1c', 0)
97
+ COM_STMT_RESET = int('0x1a', 0)
97
98
  COM_STMT_CLOSE = int('0x19', 0)
98
99
  COM_FIELD_LIST = int('0x04', 0) # deprecated
99
100