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.
- mindsdb/__about__.py +1 -1
- mindsdb/api/executor/command_executor.py +62 -61
- mindsdb/api/executor/data_types/answer.py +9 -12
- mindsdb/api/executor/datahub/classes/response.py +11 -0
- mindsdb/api/executor/datahub/datanodes/datanode.py +4 -4
- mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +7 -9
- mindsdb/api/executor/datahub/datanodes/integration_datanode.py +22 -16
- mindsdb/api/executor/datahub/datanodes/project_datanode.py +20 -20
- mindsdb/api/executor/planner/plan_join.py +1 -1
- mindsdb/api/executor/planner/steps.py +2 -1
- mindsdb/api/executor/sql_query/result_set.py +10 -7
- mindsdb/api/executor/sql_query/sql_query.py +36 -82
- mindsdb/api/executor/sql_query/steps/delete_step.py +2 -3
- mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +5 -3
- mindsdb/api/executor/sql_query/steps/insert_step.py +2 -2
- mindsdb/api/executor/sql_query/steps/prepare_steps.py +2 -2
- mindsdb/api/executor/sql_query/steps/subselect_step.py +20 -8
- mindsdb/api/executor/sql_query/steps/update_step.py +4 -6
- mindsdb/api/http/namespaces/sql.py +4 -1
- mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/ok_packet.py +1 -1
- mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +4 -27
- mindsdb/api/mysql/mysql_proxy/libs/constants/mysql.py +1 -0
- mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +38 -37
- mindsdb/integrations/handlers/chromadb_handler/chromadb_handler.py +23 -13
- mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +1 -1
- mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +3 -2
- mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +4 -4
- mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +19 -5
- mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +9 -4
- mindsdb/integrations/handlers/redshift_handler/redshift_handler.py +1 -1
- mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +18 -11
- mindsdb/integrations/libs/ml_handler_process/learn_process.py +1 -2
- mindsdb/integrations/libs/response.py +9 -4
- mindsdb/integrations/libs/vectordatabase_handler.py +17 -5
- mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +8 -98
- mindsdb/interfaces/database/log.py +8 -9
- mindsdb/interfaces/database/projects.py +1 -5
- mindsdb/interfaces/functions/controller.py +59 -17
- mindsdb/interfaces/functions/to_markdown.py +194 -0
- mindsdb/interfaces/jobs/jobs_controller.py +3 -3
- mindsdb/interfaces/knowledge_base/controller.py +101 -60
- mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +3 -14
- mindsdb/interfaces/query_context/context_controller.py +3 -1
- {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.0.dist-info}/METADATA +231 -230
- {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.0.dist-info}/RECORD +48 -46
- {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.0.dist-info}/WHEEL +0 -0
- {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.0.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
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
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
202
|
-
'success': True,
|
|
203
|
-
'result': result
|
|
204
|
-
}
|
|
191
|
+
statement_info = self.planner.get_statement_info()
|
|
205
192
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
50
|
+
response = dn.query(ds_query, session=self.session)
|
|
51
51
|
|
|
52
52
|
data = ResultSet()
|
|
53
|
-
for column in
|
|
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
|
|
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
|
|
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 = {
|
|
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'
|
|
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
|
-
|
|
130
|
-
self.
|
|
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
|
|
@@ -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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
546
|
+
if executor_answer.data is None:
|
|
553
547
|
resp = SQLAnswer(
|
|
554
548
|
resp_type=RESPONSE_TYPE.OK,
|
|
555
|
-
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=
|
|
561
|
-
columns=self.to_mysql_columns(
|
|
562
|
-
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
|
-
|
|
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=
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
642
|
+
if executor_answer.data is None:
|
|
644
643
|
resp = SQLAnswer(
|
|
645
|
-
resp_type=RESPONSE_TYPE.OK, 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(
|
|
651
|
-
for row in
|
|
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(
|
|
655
|
+
prepared_stmt["fetched"] += len(executor_answer.data[fetched:limit])
|
|
657
656
|
|
|
658
|
-
if len(
|
|
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))
|