MindsDB 25.4.1.0__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 (48) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/api/executor/command_executor.py +62 -61
  3. mindsdb/api/executor/data_types/answer.py +9 -12
  4. mindsdb/api/executor/datahub/classes/response.py +11 -0
  5. mindsdb/api/executor/datahub/datanodes/datanode.py +4 -4
  6. mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +7 -9
  7. mindsdb/api/executor/datahub/datanodes/integration_datanode.py +22 -16
  8. mindsdb/api/executor/datahub/datanodes/project_datanode.py +20 -20
  9. mindsdb/api/executor/planner/plan_join.py +1 -1
  10. mindsdb/api/executor/planner/steps.py +2 -1
  11. mindsdb/api/executor/sql_query/result_set.py +10 -7
  12. mindsdb/api/executor/sql_query/sql_query.py +36 -82
  13. mindsdb/api/executor/sql_query/steps/delete_step.py +2 -3
  14. mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +5 -3
  15. mindsdb/api/executor/sql_query/steps/insert_step.py +2 -2
  16. mindsdb/api/executor/sql_query/steps/prepare_steps.py +2 -2
  17. mindsdb/api/executor/sql_query/steps/subselect_step.py +20 -8
  18. mindsdb/api/executor/sql_query/steps/update_step.py +4 -6
  19. mindsdb/api/http/namespaces/sql.py +4 -1
  20. mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/ok_packet.py +1 -1
  21. mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +4 -27
  22. mindsdb/api/mysql/mysql_proxy/libs/constants/mysql.py +1 -0
  23. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +38 -37
  24. mindsdb/integrations/handlers/chromadb_handler/chromadb_handler.py +23 -13
  25. mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +1 -1
  26. mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +3 -2
  27. mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +4 -4
  28. mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +19 -5
  29. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +9 -4
  30. mindsdb/integrations/handlers/redshift_handler/redshift_handler.py +1 -1
  31. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +18 -11
  32. mindsdb/integrations/libs/ml_handler_process/learn_process.py +1 -2
  33. mindsdb/integrations/libs/response.py +9 -4
  34. mindsdb/integrations/libs/vectordatabase_handler.py +17 -5
  35. mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +8 -98
  36. mindsdb/interfaces/database/log.py +8 -9
  37. mindsdb/interfaces/database/projects.py +1 -5
  38. mindsdb/interfaces/functions/controller.py +59 -17
  39. mindsdb/interfaces/functions/to_markdown.py +194 -0
  40. mindsdb/interfaces/jobs/jobs_controller.py +3 -3
  41. mindsdb/interfaces/knowledge_base/controller.py +101 -60
  42. mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +3 -14
  43. mindsdb/interfaces/query_context/context_controller.py +3 -1
  44. {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.0.dist-info}/METADATA +231 -230
  45. {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.0.dist-info}/RECORD +48 -46
  46. {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.0.dist-info}/WHEEL +0 -0
  47. {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.0.dist-info}/licenses/LICENSE +0 -0
  48. {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.0.dist-info}/top_level.txt +0 -0
@@ -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 = {
@@ -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
 
@@ -21,7 +21,8 @@ import sys
21
21
  import tempfile
22
22
  import traceback
23
23
  from functools import partial
24
- from typing import Dict, List
24
+ from typing import Dict, List, Optional
25
+ from dataclasses import dataclass
25
26
 
26
27
  from numpy import dtype as np_dtype
27
28
  from pandas.api import types as pd_types
@@ -71,6 +72,7 @@ from mindsdb.api.mysql.mysql_proxy.libs.constants.mysql import (
71
72
  TYPES,
72
73
  getConstName,
73
74
  )
75
+ from mindsdb.api.executor.data_types.answer import ExecuteAnswer
74
76
  from mindsdb.api.executor.data_types.response_type import RESPONSE_TYPE
75
77
  from mindsdb.api.mysql.mysql_proxy.utilities import (
76
78
  ErWrongCharset,
@@ -93,24 +95,16 @@ def empty_fn():
93
95
  pass
94
96
 
95
97
 
98
+ @dataclass
96
99
  class SQLAnswer:
97
- def __init__(
98
- self,
99
- resp_type: RESPONSE_TYPE,
100
- columns: List[Dict] = None,
101
- data: List[Dict] = None,
102
- status: int = None,
103
- state_track: List[List] = None,
104
- error_code: int = None,
105
- error_message: str = None,
106
- ):
107
- self.resp_type = resp_type
108
- self.columns = columns
109
- self.data = data
110
- self.status = status
111
- self.state_track = state_track
112
- self.error_code = error_code
113
- self.error_message = error_message
100
+ resp_type: RESPONSE_TYPE = RESPONSE_TYPE.OK
101
+ columns: Optional[List[Dict]] = None
102
+ data: Optional[List[Dict]] = None # resultSet ?
103
+ status: Optional[int] = None
104
+ state_track: Optional[List[List]] = None
105
+ error_code: Optional[int] = None
106
+ error_message: Optional[str] = None
107
+ affected_rows: Optional[int] = None
114
108
 
115
109
  @property
116
110
  def type(self):
@@ -333,7 +327,7 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
333
327
  packages.append(self.last_packet())
334
328
  self.send_package_group(packages)
335
329
  elif answer.type == RESPONSE_TYPE.OK:
336
- self.packet(OkPacket, state_track=answer.state_track).send()
330
+ self.packet(OkPacket, state_track=answer.state_track, affected_rows=answer.affected_rows).send()
337
331
  elif answer.type == RESPONSE_TYPE.ERROR:
338
332
  self.packet(
339
333
  ErrPacket, err_code=answer.error_code, msg=answer.error_message
@@ -546,21 +540,23 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
546
540
  @profiler.profile()
547
541
  def process_query(self, sql):
548
542
  executor = Executor(session=self.session, sqlserver=self)
549
-
550
543
  executor.query_execute(sql)
544
+ executor_answer = executor.executor_answer
551
545
 
552
- if executor.data is None:
546
+ if executor_answer.data is None:
553
547
  resp = SQLAnswer(
554
548
  resp_type=RESPONSE_TYPE.OK,
555
- state_track=executor.state_track,
549
+ state_track=executor_answer.state_track,
550
+ affected_rows=executor_answer.affected_rows
556
551
  )
557
552
  else:
558
553
  resp = SQLAnswer(
559
554
  resp_type=RESPONSE_TYPE.TABLE,
560
- state_track=executor.state_track,
561
- columns=self.to_mysql_columns(executor.columns),
562
- data=executor.data,
555
+ state_track=executor_answer.state_track,
556
+ columns=self.to_mysql_columns(executor_answer.data.columns),
557
+ data=executor_answer.data,
563
558
  status=executor.server_status,
559
+ affected_rows=executor_answer.affected_rows
564
560
  )
565
561
 
566
562
  # Increment the counter and include metadata in attributes
@@ -604,18 +600,20 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
604
600
 
605
601
  def answer_stmt_execute(self, stmt_id, parameters):
606
602
  prepared_stmt = self.session.prepared_stmts[stmt_id]
607
- executor = prepared_stmt["statement"]
603
+ executor: Executor = prepared_stmt["statement"]
608
604
 
609
605
  executor.stmt_execute(parameters)
610
606
 
611
- if executor.data is None:
607
+ executor_answer: ExecuteAnswer = executor.executor_answer
608
+
609
+ if executor_answer.data is None:
612
610
  resp = SQLAnswer(
613
- resp_type=RESPONSE_TYPE.OK, state_track=executor.state_track
611
+ resp_type=RESPONSE_TYPE.OK, state_track=executor_answer.state_track
614
612
  )
615
613
  return self.send_query_answer(resp)
616
614
 
617
615
  # TODO prepared_stmt['type'] == 'lock' is not used but it works
618
- columns_def = self.to_mysql_columns(executor.columns)
616
+ columns_def = self.to_mysql_columns(executor_answer.data.columns)
619
617
  packages = [self.packet(ColumnCountPacket, count=len(columns_def))]
620
618
 
621
619
  packages.extend(self._get_column_defenition_packets(columns_def))
@@ -624,14 +622,14 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
624
622
  packages.append(self.packet(EofPacket, status=0x0062))
625
623
 
626
624
  # send all
627
- for row in executor.data.to_lists():
625
+ for row in executor_answer.data.to_lists():
628
626
  packages.append(
629
627
  self.packet(BinaryResultsetRowPacket, data=row, columns=columns_def)
630
628
  )
631
629
 
632
630
  server_status = executor.server_status or 0x0002
633
631
  packages.append(self.last_packet(status=server_status))
634
- prepared_stmt["fetched"] += len(executor.data)
632
+ prepared_stmt["fetched"] += len(executor_answer.data)
635
633
 
636
634
  return self.send_package_group(packages)
637
635
 
@@ -639,23 +637,24 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
639
637
  prepared_stmt = self.session.prepared_stmts[stmt_id]
640
638
  executor = prepared_stmt["statement"]
641
639
  fetched = prepared_stmt["fetched"]
640
+ executor_answer: ExecuteAnswer = executor.executor_answer
642
641
 
643
- if executor.data is None:
642
+ if executor_answer.data is None:
644
643
  resp = SQLAnswer(
645
- resp_type=RESPONSE_TYPE.OK, state_track=executor.state_track
644
+ resp_type=RESPONSE_TYPE.OK, state_track=executor_answer.state_track
646
645
  )
647
646
  return self.send_query_answer(resp)
648
647
 
649
648
  packages = []
650
- columns = self.to_mysql_columns(executor.columns)
651
- for row in executor.data[fetched:limit].to_lists():
649
+ columns = self.to_mysql_columns(executor_answer.data.columns)
650
+ for row in executor_answer.data[fetched:limit].to_lists():
652
651
  packages.append(
653
652
  self.packet(BinaryResultsetRowPacket, data=row, columns=columns)
654
653
  )
655
654
 
656
- prepared_stmt["fetched"] += len(executor.data[fetched:limit])
655
+ prepared_stmt["fetched"] += len(executor_answer.data[fetched:limit])
657
656
 
658
- if len(executor.data) <= limit + fetched:
657
+ if len(executor_answer.data) <= limit + fetched:
659
658
  status = sum(
660
659
  [
661
660
  SERVER_STATUS.SERVER_STATUS_AUTOCOMMIT,
@@ -772,6 +771,8 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
772
771
  elif p.type.value == COMMANDS.COM_FIELD_LIST:
773
772
  # this command is deprecated, but console client still use it.
774
773
  response = SQLAnswer(RESPONSE_TYPE.OK)
774
+ elif p.type.value == COMMANDS.COM_STMT_RESET:
775
+ response = SQLAnswer(RESPONSE_TYPE.OK)
775
776
  else:
776
777
  logger.warning("Command has no specific handler, return OK msg")
777
778
  logger.debug(str(p))