MindsDB 25.8.3.0__py3-none-any.whl → 25.9.1.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/__main__.py +2 -44
- mindsdb/api/a2a/__init__.py +52 -0
- mindsdb/api/a2a/agent.py +11 -12
- mindsdb/api/a2a/common/server/server.py +17 -36
- mindsdb/api/a2a/common/server/task_manager.py +14 -28
- mindsdb/api/a2a/task_manager.py +20 -21
- mindsdb/api/a2a/utils.py +1 -1
- mindsdb/api/common/middleware.py +106 -0
- mindsdb/api/http/initialize.py +13 -15
- mindsdb/api/http/namespaces/auth.py +6 -14
- mindsdb/api/http/namespaces/config.py +0 -2
- mindsdb/api/http/namespaces/default.py +74 -106
- mindsdb/api/http/start.py +25 -44
- mindsdb/api/litellm/start.py +11 -10
- mindsdb/api/mcp/__init__.py +165 -0
- mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +33 -64
- mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +86 -85
- mindsdb/integrations/handlers/crate_handler/crate_handler.py +3 -7
- mindsdb/integrations/handlers/derby_handler/derby_handler.py +32 -34
- mindsdb/integrations/handlers/documentdb_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/dummy_data_handler/dummy_data_handler.py +12 -13
- mindsdb/integrations/handlers/google_books_handler/google_books_handler.py +45 -44
- mindsdb/integrations/handlers/google_calendar_handler/google_calendar_handler.py +101 -95
- mindsdb/integrations/handlers/google_content_shopping_handler/google_content_shopping_handler.py +129 -129
- mindsdb/integrations/handlers/google_fit_handler/google_fit_handler.py +59 -43
- mindsdb/integrations/handlers/google_search_handler/google_search_handler.py +38 -39
- mindsdb/integrations/handlers/informix_handler/informix_handler.py +5 -18
- mindsdb/integrations/handlers/maxdb_handler/maxdb_handler.py +22 -28
- mindsdb/integrations/handlers/monetdb_handler/monetdb_handler.py +3 -7
- mindsdb/integrations/handlers/mongodb_handler/mongodb_handler.py +53 -67
- mindsdb/integrations/handlers/mongodb_handler/requirements.txt +1 -0
- mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_ast.py +43 -68
- mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_parser.py +17 -25
- mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_query.py +10 -16
- mindsdb/integrations/handlers/mongodb_handler/utils/mongodb_render.py +43 -69
- mindsdb/integrations/libs/base.py +1 -1
- mindsdb/interfaces/agents/constants.py +1 -0
- mindsdb/interfaces/knowledge_base/controller.py +3 -1
- mindsdb/utilities/config.py +3 -155
- mindsdb/utilities/log.py +0 -25
- mindsdb/utilities/starters.py +0 -39
- {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.0.dist-info}/METADATA +263 -261
- {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.0.dist-info}/RECORD +47 -91
- mindsdb/api/a2a/__main__.py +0 -144
- mindsdb/api/a2a/run_a2a.py +0 -86
- mindsdb/api/common/check_auth.py +0 -42
- mindsdb/api/http/gunicorn_wrapper.py +0 -17
- mindsdb/api/mcp/start.py +0 -205
- mindsdb/api/mongo/__init__.py +0 -0
- mindsdb/api/mongo/classes/__init__.py +0 -5
- mindsdb/api/mongo/classes/query_sql.py +0 -19
- mindsdb/api/mongo/classes/responder.py +0 -45
- mindsdb/api/mongo/classes/responder_collection.py +0 -34
- mindsdb/api/mongo/classes/scram.py +0 -86
- mindsdb/api/mongo/classes/session.py +0 -23
- mindsdb/api/mongo/functions/__init__.py +0 -19
- mindsdb/api/mongo/responders/__init__.py +0 -73
- mindsdb/api/mongo/responders/add_shard.py +0 -13
- mindsdb/api/mongo/responders/aggregate.py +0 -90
- mindsdb/api/mongo/responders/buildinfo.py +0 -17
- mindsdb/api/mongo/responders/coll_stats.py +0 -63
- mindsdb/api/mongo/responders/company_id.py +0 -25
- mindsdb/api/mongo/responders/connection_status.py +0 -22
- mindsdb/api/mongo/responders/count.py +0 -21
- mindsdb/api/mongo/responders/db_stats.py +0 -32
- mindsdb/api/mongo/responders/delete.py +0 -105
- mindsdb/api/mongo/responders/describe.py +0 -23
- mindsdb/api/mongo/responders/end_sessions.py +0 -13
- mindsdb/api/mongo/responders/find.py +0 -175
- mindsdb/api/mongo/responders/get_cmd_line_opts.py +0 -18
- mindsdb/api/mongo/responders/get_free_monitoring_status.py +0 -14
- mindsdb/api/mongo/responders/get_parameter.py +0 -23
- mindsdb/api/mongo/responders/getlog.py +0 -14
- mindsdb/api/mongo/responders/host_info.py +0 -28
- mindsdb/api/mongo/responders/insert.py +0 -270
- mindsdb/api/mongo/responders/is_master.py +0 -20
- mindsdb/api/mongo/responders/is_master_lower.py +0 -13
- mindsdb/api/mongo/responders/list_collections.py +0 -55
- mindsdb/api/mongo/responders/list_databases.py +0 -37
- mindsdb/api/mongo/responders/list_indexes.py +0 -22
- mindsdb/api/mongo/responders/ping.py +0 -13
- mindsdb/api/mongo/responders/recv_chunk_start.py +0 -13
- mindsdb/api/mongo/responders/replsetgetstatus.py +0 -13
- mindsdb/api/mongo/responders/sasl_continue.py +0 -34
- mindsdb/api/mongo/responders/sasl_start.py +0 -33
- mindsdb/api/mongo/responders/update_range_deletions.py +0 -12
- mindsdb/api/mongo/responders/whatsmyuri.py +0 -18
- mindsdb/api/mongo/server.py +0 -388
- mindsdb/api/mongo/start.py +0 -15
- mindsdb/api/mongo/utilities/__init__.py +0 -0
- {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.0.dist-info}/WHEEL +0 -0
- {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.0.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.0.dist-info}/top_level.txt +0 -0
mindsdb/api/mcp/__init__.py
CHANGED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from textwrap import dedent
|
|
2
|
+
from typing import Any
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
|
+
from collections.abc import AsyncIterator
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from mcp.server.fastmcp import FastMCP
|
|
8
|
+
from starlette.requests import Request
|
|
9
|
+
from starlette.responses import JSONResponse
|
|
10
|
+
|
|
11
|
+
from mindsdb.api.mysql.mysql_proxy.classes.fake_mysql_proxy import FakeMysqlProxy
|
|
12
|
+
from mindsdb.api.executor.data_types.response_type import RESPONSE_TYPE as SQL_RESPONSE_TYPE
|
|
13
|
+
from mindsdb.interfaces.storage import db
|
|
14
|
+
from mindsdb.utilities import log
|
|
15
|
+
|
|
16
|
+
logger = log.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class AppContext:
|
|
21
|
+
db: Any
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@asynccontextmanager
|
|
25
|
+
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
|
|
26
|
+
"""Manage application lifecycle with type-safe context"""
|
|
27
|
+
# Initialize on startup
|
|
28
|
+
db.init()
|
|
29
|
+
try:
|
|
30
|
+
yield AppContext(db=db)
|
|
31
|
+
finally:
|
|
32
|
+
# TODO: We need better way to handle this in storage/db.py
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Configure server with lifespan
|
|
37
|
+
mcp = FastMCP(
|
|
38
|
+
"MindsDB",
|
|
39
|
+
lifespan=app_lifespan,
|
|
40
|
+
dependencies=["mindsdb"], # Add any additional dependencies
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# MCP Queries
|
|
45
|
+
LISTING_QUERY = "SHOW DATABASES"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
query_tool_description = dedent("""\
|
|
49
|
+
Executes a SQL query against MindsDB.
|
|
50
|
+
|
|
51
|
+
A database must be specified either in the `context` parameter or directly in the query string (e.g., `SELECT * FROM my_database.my_table`). Queries like `SELECT * FROM my_table` will fail without a `context`.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
query (str): The SQL query to execute.
|
|
55
|
+
context (dict, optional): The default database context. For example, `{"db": "my_postgres"}`.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
A dictionary describing the result.
|
|
59
|
+
- For a successful query with no data to return (e.g., an `UPDATE` statement), the response is `{"type": "ok"}`.
|
|
60
|
+
- If the query returns tabular data, the response is a dictionary containing `data` (a list of rows) and `column_names` (a list of column names). For example: `{"type": "table", "data": [[1, "a"], [2, "b"]], "column_names": ["column_a", "column_b"]}`.
|
|
61
|
+
- In case of an error, a response is `{"type": "error", "error_message": "the error message"}`.
|
|
62
|
+
""")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@mcp.tool(name="query", description=query_tool_description)
|
|
66
|
+
def query(query: str, context: dict | None = None) -> dict[str, Any]:
|
|
67
|
+
"""Execute a SQL query against MindsDB
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
query: The SQL query to execute
|
|
71
|
+
context: Optional context parameters for the query
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Dict containing the query results or error information
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
if context is None:
|
|
78
|
+
context = {}
|
|
79
|
+
|
|
80
|
+
logger.debug(f"Incoming MCP query: {query}")
|
|
81
|
+
|
|
82
|
+
mysql_proxy = FakeMysqlProxy()
|
|
83
|
+
mysql_proxy.set_context(context)
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
result = mysql_proxy.process_query(query)
|
|
87
|
+
|
|
88
|
+
if result.type == SQL_RESPONSE_TYPE.OK:
|
|
89
|
+
return {"type": SQL_RESPONSE_TYPE.OK}
|
|
90
|
+
|
|
91
|
+
if result.type == SQL_RESPONSE_TYPE.TABLE:
|
|
92
|
+
return {
|
|
93
|
+
"type": SQL_RESPONSE_TYPE.TABLE,
|
|
94
|
+
"data": result.result_set.to_lists(json_types=True),
|
|
95
|
+
"column_names": [column.alias or column.name for column in result.result_set.columns],
|
|
96
|
+
}
|
|
97
|
+
else:
|
|
98
|
+
return {"type": SQL_RESPONSE_TYPE.ERROR, "error_code": 0, "error_message": "Unknown response type"}
|
|
99
|
+
|
|
100
|
+
except Exception as e:
|
|
101
|
+
logger.error(f"Error processing query: {str(e)}")
|
|
102
|
+
return {"type": SQL_RESPONSE_TYPE.ERROR, "error_code": 0, "error_message": str(e)}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
list_databases_tool_description = (
|
|
106
|
+
"Returns a list of all database connections currently available in MindsDB. "
|
|
107
|
+
+ "The tool takes no parameters and responds with a list of database names, "
|
|
108
|
+
+ 'for example: ["my_postgres", "my_mysql", "test_db"].'
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@mcp.tool(name="list_databases", description=list_databases_tool_description)
|
|
113
|
+
def list_databases() -> list[str]:
|
|
114
|
+
"""
|
|
115
|
+
List all databases in MindsDB
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
list[str]: list of databases
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
mysql_proxy = FakeMysqlProxy()
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
result = mysql_proxy.process_query(LISTING_QUERY)
|
|
125
|
+
if result.type == SQL_RESPONSE_TYPE.ERROR:
|
|
126
|
+
return {
|
|
127
|
+
"type": "error",
|
|
128
|
+
"error_code": result.error_code,
|
|
129
|
+
"error_message": result.error_message,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
elif result.type == SQL_RESPONSE_TYPE.OK:
|
|
133
|
+
return {"type": "ok"}
|
|
134
|
+
|
|
135
|
+
elif result.type == SQL_RESPONSE_TYPE.TABLE:
|
|
136
|
+
data = result.result_set.to_lists(json_types=True)
|
|
137
|
+
data = [val[0] for val in data]
|
|
138
|
+
return data
|
|
139
|
+
|
|
140
|
+
except Exception as e:
|
|
141
|
+
return {
|
|
142
|
+
"type": "error",
|
|
143
|
+
"error_code": 0,
|
|
144
|
+
"error_message": str(e),
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _get_status(request: Request) -> JSONResponse:
|
|
149
|
+
"""
|
|
150
|
+
Status endpoint that returns basic server information.
|
|
151
|
+
This endpoint can be used by the frontend to check if the MCP server is running.
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
status_info = {
|
|
155
|
+
"status": "ok",
|
|
156
|
+
"service": "mindsdb-mcp",
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return JSONResponse(status_info)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def get_mcp_app():
|
|
163
|
+
app = mcp.sse_app()
|
|
164
|
+
app.add_route("/status", _get_status, methods=["GET"])
|
|
165
|
+
return app
|
|
@@ -63,7 +63,7 @@ from mindsdb.api.mysql.mysql_proxy.libs.constants.mysql import (
|
|
|
63
63
|
NULL_VALUE,
|
|
64
64
|
COMMANDS,
|
|
65
65
|
ERR,
|
|
66
|
-
getConstName
|
|
66
|
+
getConstName,
|
|
67
67
|
)
|
|
68
68
|
from mindsdb.api.executor.data_types.answer import ExecuteAnswer
|
|
69
69
|
from mindsdb.api.executor.data_types.response_type import RESPONSE_TYPE
|
|
@@ -73,7 +73,7 @@ from mindsdb.api.mysql.mysql_proxy.utilities import (
|
|
|
73
73
|
)
|
|
74
74
|
from mindsdb.api.executor import exceptions as exec_exc
|
|
75
75
|
|
|
76
|
-
from mindsdb.api.common.
|
|
76
|
+
from mindsdb.api.common.middleware import check_auth
|
|
77
77
|
from mindsdb.api.mysql.mysql_proxy.libs.constants.mysql import MYSQL_DATA_TYPE
|
|
78
78
|
from mindsdb.api.executor.sql_query.result_set import Column, ResultSet
|
|
79
79
|
from mindsdb.utilities import log
|
|
@@ -116,10 +116,7 @@ class SQLAnswer:
|
|
|
116
116
|
return {
|
|
117
117
|
"type": RESPONSE_TYPE.TABLE,
|
|
118
118
|
"data": data,
|
|
119
|
-
"column_names": [
|
|
120
|
-
column.alias or column.name
|
|
121
|
-
for column in self.result_set.columns
|
|
122
|
-
],
|
|
119
|
+
"column_names": [column.alias or column.name for column in self.result_set.columns],
|
|
123
120
|
}
|
|
124
121
|
elif self.resp_type == RESPONSE_TYPE.ERROR:
|
|
125
122
|
return {
|
|
@@ -149,17 +146,13 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
149
146
|
super().__init__(request, client_address, server)
|
|
150
147
|
|
|
151
148
|
def init_session(self):
|
|
152
|
-
logger.debug(
|
|
153
|
-
"New connection [{ip}:{port}]".format(
|
|
154
|
-
ip=self.client_address[0], port=self.client_address[1]
|
|
155
|
-
)
|
|
156
|
-
)
|
|
149
|
+
logger.debug("New connection [{ip}:{port}]".format(ip=self.client_address[0], port=self.client_address[1]))
|
|
157
150
|
|
|
158
151
|
if self.server.connection_id >= 65025:
|
|
159
152
|
self.server.connection_id = 0
|
|
160
153
|
self.server.connection_id += 1
|
|
161
154
|
self.connection_id = self.server.connection_id
|
|
162
|
-
self.session = SessionController(api_type=
|
|
155
|
+
self.session = SessionController(api_type="sql")
|
|
163
156
|
|
|
164
157
|
if hasattr(self.server, "salt") and isinstance(self.server.salt, str):
|
|
165
158
|
self.salt = self.server.salt
|
|
@@ -223,10 +216,9 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
223
216
|
self.session.is_ssl = True
|
|
224
217
|
|
|
225
218
|
ssl_context = ssl.SSLContext()
|
|
219
|
+
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
|
|
226
220
|
ssl_context.load_cert_chain(self.server.cert_path)
|
|
227
|
-
ssl_socket = ssl_context.wrap_socket(
|
|
228
|
-
self.socket, server_side=True, do_handshake_on_connect=True
|
|
229
|
-
)
|
|
221
|
+
ssl_socket = ssl_context.wrap_socket(self.socket, server_side=True, do_handshake_on_connect=True)
|
|
230
222
|
|
|
231
223
|
self.socket = ssl_socket
|
|
232
224
|
handshake_resp = self.packet(HandshakeResponsePacket)
|
|
@@ -245,10 +237,7 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
245
237
|
else "mysql_native_password"
|
|
246
238
|
)
|
|
247
239
|
|
|
248
|
-
if
|
|
249
|
-
new_method == "caching_sha2_password"
|
|
250
|
-
and self.session.is_ssl is False
|
|
251
|
-
):
|
|
240
|
+
if new_method == "caching_sha2_password" and self.session.is_ssl is False:
|
|
252
241
|
logger.warning(
|
|
253
242
|
f"Check auth, user={username}, ssl={self.session.is_ssl}, auth_method={client_auth_plugin}: "
|
|
254
243
|
"error: cant switch to caching_sha2_password without SSL"
|
|
@@ -307,9 +296,7 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
307
296
|
f"connecting to database {self.session.database}"
|
|
308
297
|
)
|
|
309
298
|
|
|
310
|
-
auth_data = self.server.check_auth(
|
|
311
|
-
username, password, scramble_func, self.salt, ctx.company_id
|
|
312
|
-
)
|
|
299
|
+
auth_data = self.server.check_auth(username, password, scramble_func, self.salt, ctx.company_id)
|
|
313
300
|
if auth_data["success"]:
|
|
314
301
|
self.session.username = auth_data["username"]
|
|
315
302
|
self.session.auth = True
|
|
@@ -349,9 +336,7 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
349
336
|
elif answer.type == RESPONSE_TYPE.OK:
|
|
350
337
|
self.packet(OkPacket, state_track=answer.state_track, affected_rows=answer.affected_rows).send()
|
|
351
338
|
elif answer.type == RESPONSE_TYPE.ERROR:
|
|
352
|
-
self.packet(
|
|
353
|
-
ErrPacket, err_code=answer.error_code, msg=answer.error_message
|
|
354
|
-
).send()
|
|
339
|
+
self.packet(ErrPacket, err_code=answer.error_code, msg=answer.error_message).send()
|
|
355
340
|
|
|
356
341
|
def _get_column_defenition_packets(self, columns: dict, data=None):
|
|
357
342
|
if data is None:
|
|
@@ -370,14 +355,14 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
370
355
|
flags = column.get("flags", 0)
|
|
371
356
|
if isinstance(flags, list):
|
|
372
357
|
flags = sum(flags)
|
|
373
|
-
if column.get(
|
|
358
|
+
if column.get("size") is None:
|
|
374
359
|
length = 1
|
|
375
360
|
for row in data:
|
|
376
361
|
if isinstance(row, dict):
|
|
377
362
|
length = max(len(str(row[column_alias])), length)
|
|
378
363
|
else:
|
|
379
364
|
length = max(len(str(row[i])), length)
|
|
380
|
-
column[
|
|
365
|
+
column["size"] = 1
|
|
381
366
|
|
|
382
367
|
packets.append(
|
|
383
368
|
self.packet(
|
|
@@ -397,7 +382,7 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
397
382
|
|
|
398
383
|
def get_table_packets(self, result_set: ResultSet, status=0):
|
|
399
384
|
data_frame, columns_dict = dump_result_set_to_mysql(result_set)
|
|
400
|
-
data = data_frame.to_dict(
|
|
385
|
+
data = data_frame.to_dict("split")["data"]
|
|
401
386
|
|
|
402
387
|
# TODO remove columns order
|
|
403
388
|
packets = [self.packet(ColumnCountPacket, count=len(columns_dict))]
|
|
@@ -431,10 +416,12 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
431
416
|
|
|
432
417
|
chunk_size = 100
|
|
433
418
|
for start in range(0, len(df), chunk_size):
|
|
434
|
-
string = b"".join(
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
419
|
+
string = b"".join(
|
|
420
|
+
[
|
|
421
|
+
self.packet(body=body, length=len(body)).accum()
|
|
422
|
+
for body in df[start : start + chunk_size].applymap(apply_f).values.sum(axis=1)
|
|
423
|
+
]
|
|
424
|
+
)
|
|
438
425
|
self.socket.sendall(string)
|
|
439
426
|
|
|
440
427
|
def decode_utf(self, text):
|
|
@@ -510,7 +497,7 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
510
497
|
resp = SQLAnswer(
|
|
511
498
|
resp_type=RESPONSE_TYPE.OK,
|
|
512
499
|
state_track=executor_answer.state_track,
|
|
513
|
-
affected_rows=executor_answer.affected_rows
|
|
500
|
+
affected_rows=executor_answer.affected_rows,
|
|
514
501
|
)
|
|
515
502
|
else:
|
|
516
503
|
resp = SQLAnswer(
|
|
@@ -519,7 +506,7 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
519
506
|
result_set=executor_answer.data,
|
|
520
507
|
status=executor.server_status,
|
|
521
508
|
affected_rows=executor_answer.affected_rows,
|
|
522
|
-
mysql_types=executor_answer.data.mysql_types
|
|
509
|
+
mysql_types=executor_answer.data.mysql_types,
|
|
523
510
|
)
|
|
524
511
|
|
|
525
512
|
# Increment the counter and include metadata in attributes
|
|
@@ -568,15 +555,13 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
568
555
|
executor_answer: ExecuteAnswer = executor.executor_answer
|
|
569
556
|
|
|
570
557
|
if executor_answer.data is None:
|
|
571
|
-
resp = SQLAnswer(
|
|
572
|
-
resp_type=RESPONSE_TYPE.OK, state_track=executor_answer.state_track
|
|
573
|
-
)
|
|
558
|
+
resp = SQLAnswer(resp_type=RESPONSE_TYPE.OK, state_track=executor_answer.state_track)
|
|
574
559
|
return self.send_query_answer(resp)
|
|
575
560
|
|
|
576
561
|
# TODO prepared_stmt['type'] == 'lock' is not used but it works
|
|
577
562
|
result_set = executor_answer.data
|
|
578
563
|
data_frame, columns_dict = dump_result_set_to_mysql(result_set)
|
|
579
|
-
data = data_frame.to_dict(
|
|
564
|
+
data = data_frame.to_dict("split")["data"]
|
|
580
565
|
|
|
581
566
|
packages = [self.packet(ColumnCountPacket, count=len(columns_dict))]
|
|
582
567
|
packages.extend(self._get_column_defenition_packets(columns_dict))
|
|
@@ -586,9 +571,7 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
586
571
|
|
|
587
572
|
# send all
|
|
588
573
|
for row in data:
|
|
589
|
-
packages.append(
|
|
590
|
-
self.packet(BinaryResultsetRowPacket, data=row, columns=columns_dict)
|
|
591
|
-
)
|
|
574
|
+
packages.append(self.packet(BinaryResultsetRowPacket, data=row, columns=columns_dict))
|
|
592
575
|
|
|
593
576
|
server_status = executor.server_status or 0x0002
|
|
594
577
|
packages.append(self.last_packet(status=server_status))
|
|
@@ -603,17 +586,13 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
603
586
|
executor_answer: ExecuteAnswer = executor.executor_answer
|
|
604
587
|
|
|
605
588
|
if executor_answer.data is None:
|
|
606
|
-
resp = SQLAnswer(
|
|
607
|
-
resp_type=RESPONSE_TYPE.OK, state_track=executor_answer.state_track
|
|
608
|
-
)
|
|
589
|
+
resp = SQLAnswer(resp_type=RESPONSE_TYPE.OK, state_track=executor_answer.state_track)
|
|
609
590
|
return self.send_query_answer(resp)
|
|
610
591
|
|
|
611
592
|
packages = []
|
|
612
593
|
columns = self.to_mysql_columns(executor_answer.data.columns)
|
|
613
594
|
for row in executor_answer.data[fetched:limit].to_lists():
|
|
614
|
-
packages.append(
|
|
615
|
-
self.packet(BinaryResultsetRowPacket, data=row, columns=columns)
|
|
616
|
-
)
|
|
595
|
+
packages.append(self.packet(BinaryResultsetRowPacket, data=row, columns=columns))
|
|
617
596
|
|
|
618
597
|
prepared_stmt["fetched"] += len(executor_answer.data[fetched:limit])
|
|
619
598
|
|
|
@@ -656,9 +635,7 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
656
635
|
else:
|
|
657
636
|
ctx.user_class = cloud_connection["user_class"]
|
|
658
637
|
ctx.email_confirmed = cloud_connection["email_confirmed"]
|
|
659
|
-
self.client_capabilities = ClentCapabilities(
|
|
660
|
-
cloud_connection["client_capabilities"]
|
|
661
|
-
)
|
|
638
|
+
self.client_capabilities = ClentCapabilities(cloud_connection["client_capabilities"])
|
|
662
639
|
self.session.database = cloud_connection["database"]
|
|
663
640
|
self.session.username = "cloud"
|
|
664
641
|
self.session.auth = True
|
|
@@ -678,9 +655,7 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
678
655
|
logger.debug("Session closed by client")
|
|
679
656
|
return
|
|
680
657
|
|
|
681
|
-
logger.debug(
|
|
682
|
-
"Command TYPE: {type}".format(type=getConstName(COMMANDS, p.type.value))
|
|
683
|
-
)
|
|
658
|
+
logger.debug("Command TYPE: {type}".format(type=getConstName(COMMANDS, p.type.value)))
|
|
684
659
|
|
|
685
660
|
command_names = {
|
|
686
661
|
COMMANDS.COM_QUERY: "COM_QUERY",
|
|
@@ -705,10 +680,8 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
705
680
|
if p.type.value == COMMANDS.COM_QUERY:
|
|
706
681
|
sql = self.decode_utf(p.sql.value)
|
|
707
682
|
sql = clear_sql(sql)
|
|
708
|
-
logger.debug(f
|
|
709
|
-
profiler.set_meta(
|
|
710
|
-
query=sql, api="mysql", environment=config.get("environment")
|
|
711
|
-
)
|
|
683
|
+
logger.debug(f"Incoming query: {sql}")
|
|
684
|
+
profiler.set_meta(query=sql, api="mysql", environment=config.get("environment"))
|
|
712
685
|
with profiler.Context("mysql_query_processing"):
|
|
713
686
|
response = self.process_query(sql)
|
|
714
687
|
elif p.type.value == COMMANDS.COM_STMT_PREPARE:
|
|
@@ -791,9 +764,7 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
791
764
|
# any other exception
|
|
792
765
|
error_type = "unexpected"
|
|
793
766
|
error_traceback = traceback.format_exc()
|
|
794
|
-
logger.error(
|
|
795
|
-
f"ERROR while executing query\n" f"{error_traceback}\n" f"{e}"
|
|
796
|
-
)
|
|
767
|
+
logger.error(f"ERROR while executing query\n{error_traceback}\n{e}")
|
|
797
768
|
error_code = ERR.ER_SYNTAX_ERROR
|
|
798
769
|
response = SQLAnswer(
|
|
799
770
|
resp_type=RESPONSE_TYPE.ERROR,
|
|
@@ -841,7 +812,7 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
841
812
|
if "db" in context:
|
|
842
813
|
self.session.database = context["db"]
|
|
843
814
|
else:
|
|
844
|
-
self.session.database = config.get(
|
|
815
|
+
self.session.database = config.get("default_project")
|
|
845
816
|
|
|
846
817
|
if "profiling" in context:
|
|
847
818
|
self.session.profiling = context["profiling"]
|
|
@@ -851,9 +822,7 @@ class MysqlProxy(SocketServer.BaseRequestHandler):
|
|
|
851
822
|
self.session.show_secrets = context["show_secrets"]
|
|
852
823
|
|
|
853
824
|
def get_context(self):
|
|
854
|
-
context = {
|
|
855
|
-
"show_secrets": self.session.show_secrets
|
|
856
|
-
}
|
|
825
|
+
context = {"show_secrets": self.session.show_secrets}
|
|
857
826
|
if self.session.database is not None:
|
|
858
827
|
context["db"] = self.session.database
|
|
859
828
|
if self.session.profiling is True:
|