MindsDB 25.9.2.0a1__py3-none-any.whl → 25.10.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of MindsDB might be problematic. Click here for more details.
- mindsdb/__about__.py +1 -1
- mindsdb/__main__.py +40 -29
- mindsdb/api/a2a/__init__.py +1 -1
- mindsdb/api/a2a/agent.py +16 -10
- mindsdb/api/a2a/common/server/server.py +7 -3
- mindsdb/api/a2a/common/server/task_manager.py +12 -5
- mindsdb/api/a2a/common/types.py +66 -0
- mindsdb/api/a2a/task_manager.py +65 -17
- mindsdb/api/common/middleware.py +10 -12
- mindsdb/api/executor/command_executor.py +51 -40
- mindsdb/api/executor/datahub/datanodes/datanode.py +2 -2
- mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +7 -13
- mindsdb/api/executor/datahub/datanodes/integration_datanode.py +101 -49
- mindsdb/api/executor/datahub/datanodes/project_datanode.py +8 -4
- mindsdb/api/executor/datahub/datanodes/system_tables.py +3 -2
- mindsdb/api/executor/exceptions.py +29 -10
- mindsdb/api/executor/planner/plan_join.py +17 -3
- mindsdb/api/executor/planner/query_prepare.py +2 -20
- mindsdb/api/executor/sql_query/sql_query.py +74 -74
- mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +1 -2
- mindsdb/api/executor/sql_query/steps/subselect_step.py +0 -1
- mindsdb/api/executor/utilities/functions.py +6 -6
- mindsdb/api/executor/utilities/sql.py +37 -20
- mindsdb/api/http/gui.py +5 -11
- mindsdb/api/http/initialize.py +75 -61
- mindsdb/api/http/namespaces/agents.py +10 -15
- mindsdb/api/http/namespaces/analysis.py +13 -20
- mindsdb/api/http/namespaces/auth.py +1 -1
- mindsdb/api/http/namespaces/chatbots.py +0 -5
- mindsdb/api/http/namespaces/config.py +15 -11
- mindsdb/api/http/namespaces/databases.py +140 -201
- mindsdb/api/http/namespaces/file.py +17 -4
- mindsdb/api/http/namespaces/handlers.py +17 -7
- mindsdb/api/http/namespaces/knowledge_bases.py +28 -7
- mindsdb/api/http/namespaces/models.py +94 -126
- mindsdb/api/http/namespaces/projects.py +13 -22
- mindsdb/api/http/namespaces/sql.py +33 -25
- mindsdb/api/http/namespaces/tab.py +27 -37
- mindsdb/api/http/namespaces/views.py +1 -1
- mindsdb/api/http/start.py +16 -10
- mindsdb/api/mcp/__init__.py +2 -1
- mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +15 -20
- mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +26 -50
- mindsdb/api/mysql/mysql_proxy/utilities/__init__.py +0 -1
- mindsdb/api/mysql/mysql_proxy/utilities/dump.py +8 -2
- mindsdb/integrations/handlers/byom_handler/byom_handler.py +165 -190
- mindsdb/integrations/handlers/databricks_handler/databricks_handler.py +98 -46
- mindsdb/integrations/handlers/druid_handler/druid_handler.py +32 -40
- mindsdb/integrations/handlers/file_handler/file_handler.py +7 -0
- mindsdb/integrations/handlers/gitlab_handler/gitlab_handler.py +5 -2
- mindsdb/integrations/handlers/lightwood_handler/functions.py +45 -79
- mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +438 -100
- mindsdb/integrations/handlers/mssql_handler/requirements_odbc.txt +3 -0
- mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +235 -3
- mindsdb/integrations/handlers/oracle_handler/__init__.py +2 -0
- mindsdb/integrations/handlers/oracle_handler/connection_args.py +7 -1
- mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +321 -16
- mindsdb/integrations/handlers/oracle_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +14 -2
- mindsdb/integrations/handlers/shopify_handler/shopify_handler.py +25 -12
- mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +2 -1
- mindsdb/integrations/handlers/statsforecast_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/statsforecast_handler/requirements_extra.txt +1 -0
- mindsdb/integrations/handlers/web_handler/urlcrawl_helpers.py +4 -4
- mindsdb/integrations/handlers/zendesk_handler/zendesk_tables.py +144 -111
- mindsdb/integrations/libs/api_handler.py +10 -10
- mindsdb/integrations/libs/base.py +4 -4
- mindsdb/integrations/libs/llm/utils.py +2 -2
- mindsdb/integrations/libs/ml_handler_process/create_engine_process.py +4 -7
- mindsdb/integrations/libs/ml_handler_process/func_call_process.py +2 -7
- mindsdb/integrations/libs/ml_handler_process/learn_process.py +37 -47
- mindsdb/integrations/libs/ml_handler_process/update_engine_process.py +4 -7
- mindsdb/integrations/libs/ml_handler_process/update_process.py +2 -7
- mindsdb/integrations/libs/process_cache.py +132 -140
- mindsdb/integrations/libs/response.py +18 -12
- mindsdb/integrations/libs/vectordatabase_handler.py +26 -0
- mindsdb/integrations/utilities/files/file_reader.py +6 -7
- mindsdb/integrations/utilities/handlers/auth_utilities/snowflake/__init__.py +1 -0
- mindsdb/integrations/utilities/handlers/auth_utilities/snowflake/snowflake_jwt_gen.py +151 -0
- mindsdb/integrations/utilities/rag/config_loader.py +37 -26
- mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +83 -30
- mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +4 -4
- mindsdb/integrations/utilities/rag/retrievers/sql_retriever.py +55 -133
- mindsdb/integrations/utilities/rag/settings.py +58 -133
- mindsdb/integrations/utilities/rag/splitters/file_splitter.py +5 -15
- mindsdb/interfaces/agents/agents_controller.py +2 -3
- mindsdb/interfaces/agents/constants.py +0 -2
- mindsdb/interfaces/agents/litellm_server.py +34 -58
- mindsdb/interfaces/agents/mcp_client_agent.py +10 -10
- mindsdb/interfaces/agents/mindsdb_database_agent.py +5 -5
- mindsdb/interfaces/agents/run_mcp_agent.py +12 -21
- mindsdb/interfaces/chatbot/chatbot_task.py +20 -23
- mindsdb/interfaces/chatbot/polling.py +30 -18
- mindsdb/interfaces/data_catalog/data_catalog_loader.py +16 -17
- mindsdb/interfaces/data_catalog/data_catalog_reader.py +15 -4
- mindsdb/interfaces/database/data_handlers_cache.py +190 -0
- mindsdb/interfaces/database/database.py +3 -3
- mindsdb/interfaces/database/integrations.py +7 -110
- mindsdb/interfaces/database/projects.py +2 -6
- mindsdb/interfaces/database/views.py +1 -4
- mindsdb/interfaces/file/file_controller.py +6 -6
- mindsdb/interfaces/functions/controller.py +1 -1
- mindsdb/interfaces/functions/to_markdown.py +2 -2
- mindsdb/interfaces/jobs/jobs_controller.py +5 -9
- mindsdb/interfaces/jobs/scheduler.py +3 -9
- mindsdb/interfaces/knowledge_base/controller.py +244 -128
- mindsdb/interfaces/knowledge_base/evaluate.py +36 -41
- mindsdb/interfaces/knowledge_base/executor.py +11 -0
- mindsdb/interfaces/knowledge_base/llm_client.py +51 -17
- mindsdb/interfaces/knowledge_base/preprocessing/json_chunker.py +40 -61
- mindsdb/interfaces/model/model_controller.py +172 -168
- mindsdb/interfaces/query_context/context_controller.py +14 -2
- mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +10 -14
- mindsdb/interfaces/skills/retrieval_tool.py +43 -50
- mindsdb/interfaces/skills/skill_tool.py +2 -2
- mindsdb/interfaces/skills/skills_controller.py +1 -4
- mindsdb/interfaces/skills/sql_agent.py +25 -19
- mindsdb/interfaces/storage/db.py +16 -6
- mindsdb/interfaces/storage/fs.py +114 -169
- mindsdb/interfaces/storage/json.py +19 -18
- mindsdb/interfaces/tabs/tabs_controller.py +49 -72
- mindsdb/interfaces/tasks/task_monitor.py +3 -9
- mindsdb/interfaces/tasks/task_thread.py +7 -9
- mindsdb/interfaces/triggers/trigger_task.py +7 -13
- mindsdb/interfaces/triggers/triggers_controller.py +47 -52
- mindsdb/migrations/migrate.py +16 -16
- mindsdb/utilities/api_status.py +58 -0
- mindsdb/utilities/config.py +68 -2
- mindsdb/utilities/exception.py +40 -1
- mindsdb/utilities/fs.py +0 -1
- mindsdb/utilities/hooks/profiling.py +17 -14
- mindsdb/utilities/json_encoder.py +24 -10
- mindsdb/utilities/langfuse.py +40 -45
- mindsdb/utilities/log.py +272 -0
- mindsdb/utilities/ml_task_queue/consumer.py +52 -58
- mindsdb/utilities/ml_task_queue/producer.py +26 -30
- mindsdb/utilities/render/sqlalchemy_render.py +22 -20
- mindsdb/utilities/starters.py +0 -10
- mindsdb/utilities/utils.py +2 -2
- {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/METADATA +293 -276
- {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/RECORD +144 -158
- mindsdb/api/mysql/mysql_proxy/utilities/exceptions.py +0 -14
- mindsdb/api/postgres/__init__.py +0 -0
- mindsdb/api/postgres/postgres_proxy/__init__.py +0 -0
- mindsdb/api/postgres/postgres_proxy/executor/__init__.py +0 -1
- mindsdb/api/postgres/postgres_proxy/executor/executor.py +0 -189
- mindsdb/api/postgres/postgres_proxy/postgres_packets/__init__.py +0 -0
- mindsdb/api/postgres/postgres_proxy/postgres_packets/errors.py +0 -322
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_fields.py +0 -34
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message.py +0 -31
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_formats.py +0 -1265
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_identifiers.py +0 -31
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_packets.py +0 -253
- mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +0 -477
- mindsdb/api/postgres/postgres_proxy/utilities/__init__.py +0 -10
- mindsdb/api/postgres/start.py +0 -11
- mindsdb/integrations/handlers/mssql_handler/tests/__init__.py +0 -0
- mindsdb/integrations/handlers/mssql_handler/tests/test_mssql_handler.py +0 -169
- mindsdb/integrations/handlers/oracle_handler/tests/__init__.py +0 -0
- mindsdb/integrations/handlers/oracle_handler/tests/test_oracle_handler.py +0 -32
- {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/WHEEL +0 -0
- {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -1,24 +1,50 @@
|
|
|
1
1
|
from typing import Text, Dict, Any, Optional
|
|
2
2
|
|
|
3
|
+
import pandas as pd
|
|
3
4
|
from databricks.sql import connect, RequestError, ServerOperationError
|
|
4
5
|
from databricks.sql.client import Connection
|
|
5
6
|
from databricks.sqlalchemy import DatabricksDialect
|
|
6
7
|
from mindsdb_sql_parser.ast.base import ASTNode
|
|
7
|
-
from mindsdb.utilities.render.sqlalchemy_render import SqlalchemyRender
|
|
8
|
-
import pandas as pd
|
|
9
8
|
|
|
9
|
+
from mindsdb.utilities.render.sqlalchemy_render import SqlalchemyRender
|
|
10
10
|
from mindsdb.integrations.libs.base import DatabaseHandler
|
|
11
11
|
from mindsdb.integrations.libs.response import (
|
|
12
12
|
HandlerStatusResponse as StatusResponse,
|
|
13
13
|
HandlerResponse as Response,
|
|
14
14
|
RESPONSE_TYPE,
|
|
15
|
+
INF_SCHEMA_COLUMNS_NAMES_SET,
|
|
15
16
|
)
|
|
16
17
|
from mindsdb.utilities import log
|
|
18
|
+
from mindsdb.api.mysql.mysql_proxy.libs.constants.mysql import MYSQL_DATA_TYPE
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
logger = log.getLogger(__name__)
|
|
20
22
|
|
|
21
23
|
|
|
24
|
+
def _map_type(internal_type_name: str | None) -> MYSQL_DATA_TYPE:
|
|
25
|
+
"""Map MyDatabricks SQL text types names to MySQL types as enum.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
internal_type_name (str): The name of the Databricks type to map.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
MYSQL_DATA_TYPE: The MySQL type enum that corresponds to the MySQL text type name.
|
|
32
|
+
"""
|
|
33
|
+
if not isinstance(internal_type_name, str):
|
|
34
|
+
return MYSQL_DATA_TYPE.TEXT
|
|
35
|
+
if internal_type_name.upper() == "STRING":
|
|
36
|
+
return MYSQL_DATA_TYPE.TEXT
|
|
37
|
+
if internal_type_name.upper() == "LONG":
|
|
38
|
+
return MYSQL_DATA_TYPE.BIGINT
|
|
39
|
+
if internal_type_name.upper() == "SHORT":
|
|
40
|
+
return MYSQL_DATA_TYPE.SMALLINT
|
|
41
|
+
try:
|
|
42
|
+
return MYSQL_DATA_TYPE(internal_type_name.upper())
|
|
43
|
+
except Exception:
|
|
44
|
+
logger.info(f"Databricks handler: unknown type: {internal_type_name}, use TEXT as fallback.")
|
|
45
|
+
return MYSQL_DATA_TYPE.TEXT
|
|
46
|
+
|
|
47
|
+
|
|
22
48
|
class DatabricksHandler(DatabaseHandler):
|
|
23
49
|
"""
|
|
24
50
|
This handler handles the connection and execution of SQL statements on Databricks.
|
|
@@ -64,11 +90,8 @@ class DatabricksHandler(DatabaseHandler):
|
|
|
64
90
|
return self.connection
|
|
65
91
|
|
|
66
92
|
# Mandatory connection parameters.
|
|
67
|
-
if not all(
|
|
68
|
-
|
|
69
|
-
for key in ["server_hostname", "http_path", "access_token"]
|
|
70
|
-
):
|
|
71
|
-
raise ValueError('Required parameters (server_hostname, http_path, access_token) must be provided.')
|
|
93
|
+
if not all(key in self.connection_data for key in ["server_hostname", "http_path", "access_token"]):
|
|
94
|
+
raise ValueError("Required parameters (server_hostname, http_path, access_token) must be provided.")
|
|
72
95
|
|
|
73
96
|
config = {
|
|
74
97
|
"server_hostname": self.connection_data["server_hostname"],
|
|
@@ -88,19 +111,17 @@ class DatabricksHandler(DatabaseHandler):
|
|
|
88
111
|
config[parameter] = self.connection_data[parameter]
|
|
89
112
|
|
|
90
113
|
try:
|
|
91
|
-
self.connection = connect(
|
|
92
|
-
**config
|
|
93
|
-
)
|
|
114
|
+
self.connection = connect(**config)
|
|
94
115
|
self.is_connected = True
|
|
95
116
|
return self.connection
|
|
96
117
|
except RequestError as request_error:
|
|
97
|
-
logger.error(f
|
|
118
|
+
logger.error(f"Request error when connecting to Databricks: {request_error}")
|
|
98
119
|
raise
|
|
99
120
|
except RuntimeError as runtime_error:
|
|
100
|
-
logger.error(f
|
|
121
|
+
logger.error(f"Runtime error when connecting to Databricks: {runtime_error}")
|
|
101
122
|
raise
|
|
102
123
|
except Exception as unknown_error:
|
|
103
|
-
logger.error(f
|
|
124
|
+
logger.error(f"Unknown error when connecting to Databricks: {unknown_error}")
|
|
104
125
|
raise
|
|
105
126
|
|
|
106
127
|
def disconnect(self):
|
|
@@ -129,7 +150,7 @@ class DatabricksHandler(DatabaseHandler):
|
|
|
129
150
|
|
|
130
151
|
# Execute a simple query to check the connection.
|
|
131
152
|
query = "SELECT 1 FROM information_schema.schemata"
|
|
132
|
-
if
|
|
153
|
+
if "schema" in self.connection_data:
|
|
133
154
|
query += f" WHERE schema_name = '{self.connection_data['schema']}'"
|
|
134
155
|
|
|
135
156
|
with connection.cursor() as cursor:
|
|
@@ -138,14 +159,14 @@ class DatabricksHandler(DatabaseHandler):
|
|
|
138
159
|
|
|
139
160
|
# If the query does not return a result, the schema does not exist.
|
|
140
161
|
if not result:
|
|
141
|
-
raise ValueError(f
|
|
162
|
+
raise ValueError(f"The schema {self.connection_data['schema']} does not exist!")
|
|
142
163
|
|
|
143
164
|
response.success = True
|
|
144
165
|
except (ValueError, RequestError, RuntimeError, ServerOperationError) as known_error:
|
|
145
|
-
logger.error(f
|
|
166
|
+
logger.error(f"Connection check to Databricks failed, {known_error}!")
|
|
146
167
|
response.error_message = str(known_error)
|
|
147
168
|
except Exception as unknown_error:
|
|
148
|
-
logger.error(f
|
|
169
|
+
logger.error(f"Connection check to Databricks failed due to an unknown error, {unknown_error}!")
|
|
149
170
|
response.error_message = str(unknown_error)
|
|
150
171
|
|
|
151
172
|
if response.success and need_to_close:
|
|
@@ -176,30 +197,18 @@ class DatabricksHandler(DatabaseHandler):
|
|
|
176
197
|
if result:
|
|
177
198
|
response = Response(
|
|
178
199
|
RESPONSE_TYPE.TABLE,
|
|
179
|
-
data_frame=pd.DataFrame(
|
|
180
|
-
result, columns=[x[0] for x in cursor.description]
|
|
181
|
-
),
|
|
200
|
+
data_frame=pd.DataFrame(result, columns=[x[0] for x in cursor.description]),
|
|
182
201
|
)
|
|
183
202
|
|
|
184
203
|
else:
|
|
185
204
|
response = Response(RESPONSE_TYPE.OK)
|
|
186
205
|
connection.commit()
|
|
187
206
|
except ServerOperationError as server_error:
|
|
188
|
-
logger.error(
|
|
189
|
-
|
|
190
|
-
)
|
|
191
|
-
response = Response(
|
|
192
|
-
RESPONSE_TYPE.ERROR,
|
|
193
|
-
error_message=str(server_error)
|
|
194
|
-
)
|
|
207
|
+
logger.error(f"Server error running query: {query} on Databricks, {server_error}!")
|
|
208
|
+
response = Response(RESPONSE_TYPE.ERROR, error_message=str(server_error))
|
|
195
209
|
except Exception as unknown_error:
|
|
196
|
-
logger.error(
|
|
197
|
-
|
|
198
|
-
)
|
|
199
|
-
response = Response(
|
|
200
|
-
RESPONSE_TYPE.ERROR,
|
|
201
|
-
error_message=str(unknown_error)
|
|
202
|
-
)
|
|
210
|
+
logger.error(f"Unknown error running query: {query} on Databricks, {unknown_error}!")
|
|
211
|
+
response = Response(RESPONSE_TYPE.ERROR, error_message=str(unknown_error))
|
|
203
212
|
|
|
204
213
|
if need_to_close is True:
|
|
205
214
|
self.disconnect()
|
|
@@ -220,29 +229,44 @@ class DatabricksHandler(DatabaseHandler):
|
|
|
220
229
|
query_str = renderer.get_string(query, with_failback=True)
|
|
221
230
|
return self.native_query(query_str)
|
|
222
231
|
|
|
223
|
-
def get_tables(self) -> Response:
|
|
232
|
+
def get_tables(self, all: bool = False) -> Response:
|
|
224
233
|
"""
|
|
225
234
|
Retrieves a list of all non-system tables in the connected schema of the Databricks workspace.
|
|
226
235
|
|
|
236
|
+
Args:
|
|
237
|
+
all (bool): If True - return tables from all schemas.
|
|
238
|
+
|
|
227
239
|
Returns:
|
|
228
240
|
Response: A response object containing a list of tables in the connected schema.
|
|
229
241
|
"""
|
|
230
|
-
|
|
231
|
-
|
|
242
|
+
all_filter = "and table_schema = current_schema()"
|
|
243
|
+
if all is True:
|
|
244
|
+
all_filter = ""
|
|
245
|
+
query = f"""
|
|
246
|
+
SELECT
|
|
247
|
+
table_schema,
|
|
248
|
+
table_name,
|
|
249
|
+
table_type
|
|
250
|
+
FROM
|
|
251
|
+
information_schema.tables
|
|
252
|
+
WHERE
|
|
253
|
+
table_schema != 'information_schema'
|
|
254
|
+
{all_filter}
|
|
232
255
|
"""
|
|
233
256
|
result = self.native_query(query)
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
257
|
+
if result.resp_type == RESPONSE_TYPE.OK:
|
|
258
|
+
result = Response(
|
|
259
|
+
RESPONSE_TYPE.TABLE, data_frame=pd.DataFrame([], columns=list(INF_SCHEMA_COLUMNS_NAMES_SET))
|
|
260
|
+
)
|
|
238
261
|
return result
|
|
239
262
|
|
|
240
|
-
def get_columns(self, table_name:
|
|
263
|
+
def get_columns(self, table_name: str, schema_name: str | None = None) -> Response:
|
|
241
264
|
"""
|
|
242
265
|
Retrieves column details for a specified table in the Databricks workspace.
|
|
243
266
|
|
|
244
267
|
Args:
|
|
245
|
-
table_name (
|
|
268
|
+
table_name (str): The name of the table for which to retrieve column information.
|
|
269
|
+
schema_name (str|None): The name of the schema in which the table is located.
|
|
246
270
|
|
|
247
271
|
Raises:
|
|
248
272
|
ValueError: If the 'table_name' is not a valid string.
|
|
@@ -253,9 +277,37 @@ class DatabricksHandler(DatabaseHandler):
|
|
|
253
277
|
if not table_name or not isinstance(table_name, str):
|
|
254
278
|
raise ValueError("Invalid table name provided.")
|
|
255
279
|
|
|
256
|
-
|
|
280
|
+
if isinstance(schema_name, str):
|
|
281
|
+
schema_name = f"'{schema_name}'"
|
|
282
|
+
else:
|
|
283
|
+
schema_name = "current_schema()"
|
|
284
|
+
query = f"""
|
|
285
|
+
SELECT
|
|
286
|
+
COLUMN_NAME,
|
|
287
|
+
DATA_TYPE,
|
|
288
|
+
ORDINAL_POSITION,
|
|
289
|
+
COLUMN_DEFAULT,
|
|
290
|
+
IS_NULLABLE,
|
|
291
|
+
CHARACTER_MAXIMUM_LENGTH,
|
|
292
|
+
CHARACTER_OCTET_LENGTH,
|
|
293
|
+
NUMERIC_PRECISION,
|
|
294
|
+
NUMERIC_SCALE,
|
|
295
|
+
DATETIME_PRECISION,
|
|
296
|
+
null as CHARACTER_SET_NAME,
|
|
297
|
+
null as COLLATION_NAME
|
|
298
|
+
FROM
|
|
299
|
+
information_schema.columns
|
|
300
|
+
WHERE
|
|
301
|
+
table_name = '{table_name}'
|
|
302
|
+
AND
|
|
303
|
+
table_schema = {schema_name}
|
|
304
|
+
"""
|
|
305
|
+
|
|
257
306
|
result = self.native_query(query)
|
|
307
|
+
if result.resp_type == RESPONSE_TYPE.OK:
|
|
308
|
+
result = Response(
|
|
309
|
+
RESPONSE_TYPE.TABLE, data_frame=pd.DataFrame([], columns=list(INF_SCHEMA_COLUMNS_NAMES_SET))
|
|
310
|
+
)
|
|
311
|
+
result.to_columns_table_response(map_type_fn=_map_type)
|
|
258
312
|
|
|
259
|
-
df = result.data_frame
|
|
260
|
-
result.data_frame = df.rename(columns={"col_name": "column_name"})
|
|
261
313
|
return result
|
|
@@ -8,13 +8,13 @@ from mindsdb.utilities.render.sqlalchemy_render import SqlalchemyRender
|
|
|
8
8
|
from mindsdb.integrations.libs.base import DatabaseHandler
|
|
9
9
|
from pydruid.db.sqlalchemy import DruidDialect
|
|
10
10
|
|
|
11
|
-
from mindsdb_sql_parser
|
|
11
|
+
from mindsdb_sql_parser import ASTNode
|
|
12
12
|
|
|
13
13
|
from mindsdb.utilities import log
|
|
14
14
|
from mindsdb.integrations.libs.response import (
|
|
15
15
|
HandlerStatusResponse as StatusResponse,
|
|
16
16
|
HandlerResponse as Response,
|
|
17
|
-
RESPONSE_TYPE
|
|
17
|
+
RESPONSE_TYPE,
|
|
18
18
|
)
|
|
19
19
|
|
|
20
20
|
logger = log.getLogger(__name__)
|
|
@@ -25,7 +25,7 @@ class DruidHandler(DatabaseHandler):
|
|
|
25
25
|
This handler handles connection and execution of the Apache Druid statements.
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
-
name =
|
|
28
|
+
name = "druid"
|
|
29
29
|
|
|
30
30
|
def __init__(self, name: str, connection_data: Optional[dict], **kwargs):
|
|
31
31
|
"""
|
|
@@ -37,18 +37,18 @@ class DruidHandler(DatabaseHandler):
|
|
|
37
37
|
"""
|
|
38
38
|
super().__init__(name)
|
|
39
39
|
self.parser = parse_sql
|
|
40
|
-
self.dialect =
|
|
40
|
+
self.dialect = "druid"
|
|
41
41
|
|
|
42
|
-
optional_parameters = [
|
|
42
|
+
optional_parameters = ["user", "password"]
|
|
43
43
|
for parameter in optional_parameters:
|
|
44
44
|
if parameter not in connection_data:
|
|
45
45
|
connection_data[parameter] = None
|
|
46
46
|
|
|
47
|
-
if
|
|
48
|
-
connection_data[
|
|
47
|
+
if "path" not in connection_data:
|
|
48
|
+
connection_data["path"] = "/druid/v2/sql/"
|
|
49
49
|
|
|
50
|
-
if
|
|
51
|
-
connection_data[
|
|
50
|
+
if "scheme" not in connection_data:
|
|
51
|
+
connection_data["scheme"] = "http"
|
|
52
52
|
|
|
53
53
|
self.connection_data = connection_data
|
|
54
54
|
self.kwargs = kwargs
|
|
@@ -71,12 +71,12 @@ class DruidHandler(DatabaseHandler):
|
|
|
71
71
|
return self.connection
|
|
72
72
|
|
|
73
73
|
self.connection = connect(
|
|
74
|
-
host=self.connection_data[
|
|
75
|
-
port=self.connection_data[
|
|
76
|
-
path=self.connection_data[
|
|
77
|
-
scheme=self.connection_data[
|
|
78
|
-
user=self.connection_data[
|
|
79
|
-
password=self.connection_data[
|
|
74
|
+
host=self.connection_data["host"],
|
|
75
|
+
port=self.connection_data["port"],
|
|
76
|
+
path=self.connection_data["path"],
|
|
77
|
+
scheme=self.connection_data["scheme"],
|
|
78
|
+
user=self.connection_data["user"],
|
|
79
|
+
password=self.connection_data["password"],
|
|
80
80
|
)
|
|
81
81
|
self.is_connected = True
|
|
82
82
|
|
|
@@ -106,11 +106,11 @@ class DruidHandler(DatabaseHandler):
|
|
|
106
106
|
|
|
107
107
|
try:
|
|
108
108
|
conn = self.connect()
|
|
109
|
-
conn.cursor().execute(
|
|
109
|
+
conn.cursor().execute("select 1") # raise exception if provided wrong credentials
|
|
110
110
|
|
|
111
111
|
response.success = True
|
|
112
112
|
except Exception as e:
|
|
113
|
-
logger.error(f
|
|
113
|
+
logger.error(f"Error connecting to Druid, {e}!")
|
|
114
114
|
response.error_message = str(e)
|
|
115
115
|
finally:
|
|
116
116
|
if response.success is True and need_to_close:
|
|
@@ -139,21 +139,14 @@ class DruidHandler(DatabaseHandler):
|
|
|
139
139
|
result = cursor.fetchall()
|
|
140
140
|
if result:
|
|
141
141
|
response = Response(
|
|
142
|
-
RESPONSE_TYPE.TABLE,
|
|
143
|
-
data_frame=pd.DataFrame(
|
|
144
|
-
result,
|
|
145
|
-
columns=[x[0] for x in cursor.description]
|
|
146
|
-
)
|
|
142
|
+
RESPONSE_TYPE.TABLE, data_frame=pd.DataFrame(result, columns=[x[0] for x in cursor.description])
|
|
147
143
|
)
|
|
148
144
|
else:
|
|
149
145
|
connection.commit()
|
|
150
146
|
response = Response(RESPONSE_TYPE.OK)
|
|
151
147
|
except Exception as e:
|
|
152
|
-
logger.error(f
|
|
153
|
-
response = Response(
|
|
154
|
-
RESPONSE_TYPE.ERROR,
|
|
155
|
-
error_message=str(e)
|
|
156
|
-
)
|
|
148
|
+
logger.error(f"Error running query: {query} on Pinot!")
|
|
149
|
+
response = Response(RESPONSE_TYPE.ERROR, error_message=str(e))
|
|
157
150
|
|
|
158
151
|
cursor.close()
|
|
159
152
|
if need_to_close is True:
|
|
@@ -182,18 +175,18 @@ class DruidHandler(DatabaseHandler):
|
|
|
182
175
|
"""
|
|
183
176
|
|
|
184
177
|
query = """
|
|
185
|
-
SELECT
|
|
178
|
+
SELECT
|
|
179
|
+
TABLE_SCHEMA AS table_schema,
|
|
180
|
+
TABLE_NAME AS table_name,
|
|
181
|
+
TABLE_TYPE AS table_type
|
|
186
182
|
FROM INFORMATION_SCHEMA.TABLES
|
|
183
|
+
WHERE TABLE_SCHEMA not in ('INFORMATION_SCHEMA', 'sys')
|
|
187
184
|
"""
|
|
188
185
|
result = self.native_query(query)
|
|
189
|
-
df = result.data_frame
|
|
190
|
-
|
|
191
|
-
df = df[['TABLE_NAME', 'TABLE_TYPE']]
|
|
192
|
-
result.data_frame = df.rename(columns={'TABLE_NAME': 'table_name', 'TABLE_TYPE': 'table_type'})
|
|
193
186
|
|
|
194
187
|
return result
|
|
195
188
|
|
|
196
|
-
def get_columns(self, table_name: str) -> StatusResponse:
|
|
189
|
+
def get_columns(self, table_name: str, schema_name: Optional[str] = None) -> StatusResponse:
|
|
197
190
|
"""
|
|
198
191
|
Returns a list of entity columns.
|
|
199
192
|
Args:
|
|
@@ -201,16 +194,15 @@ class DruidHandler(DatabaseHandler):
|
|
|
201
194
|
Returns:
|
|
202
195
|
HandlerResponse
|
|
203
196
|
"""
|
|
204
|
-
|
|
197
|
+
if schema_name is None:
|
|
198
|
+
schema_name = "druid"
|
|
205
199
|
query = f"""
|
|
206
|
-
SELECT
|
|
200
|
+
SELECT
|
|
201
|
+
COLUMN_NAME FIELD,
|
|
202
|
+
DATA_TYPE TYPE
|
|
207
203
|
FROM INFORMATION_SCHEMA.COLUMNS
|
|
208
|
-
WHERE "TABLE_SCHEMA" = '
|
|
204
|
+
WHERE "TABLE_SCHEMA" = '{schema_name}' AND "TABLE_NAME" = '{table_name}'
|
|
209
205
|
"""
|
|
210
206
|
result = self.native_query(query)
|
|
211
|
-
df = result.data_frame
|
|
212
|
-
|
|
213
|
-
df = df[['COLUMN_NAME', 'DATA_TYPE']]
|
|
214
|
-
result.data_frame = df.rename(columns={'COLUMN_NAME': 'column_name', 'DATA_TYPE': 'data_type'})
|
|
215
207
|
|
|
216
208
|
return result
|
|
@@ -50,6 +50,7 @@ class FileHandler(DatabaseHandler):
|
|
|
50
50
|
self.chunk_size = connection_data.get("chunk_size", DEFAULT_CHUNK_SIZE)
|
|
51
51
|
self.chunk_overlap = connection_data.get("chunk_overlap", DEFAULT_CHUNK_OVERLAP)
|
|
52
52
|
self.file_controller = file_controller
|
|
53
|
+
self.thread_safe = True
|
|
53
54
|
|
|
54
55
|
def connect(self, **kwargs):
|
|
55
56
|
return
|
|
@@ -83,6 +84,12 @@ class FileHandler(DatabaseHandler):
|
|
|
83
84
|
table_name = table_identifier.parts[-1]
|
|
84
85
|
try:
|
|
85
86
|
self.file_controller.delete_file(table_name)
|
|
87
|
+
except FileNotFoundError as e:
|
|
88
|
+
if not query.if_exists:
|
|
89
|
+
return Response(
|
|
90
|
+
RESPONSE_TYPE.ERROR,
|
|
91
|
+
error_message=f"Can't delete table '{table_name}': {e}",
|
|
92
|
+
)
|
|
86
93
|
except Exception as e:
|
|
87
94
|
return Response(
|
|
88
95
|
RESPONSE_TYPE.ERROR,
|
|
@@ -16,7 +16,7 @@ class GitlabHandler(APIHandler):
|
|
|
16
16
|
"""The GitLab handler implementation"""
|
|
17
17
|
|
|
18
18
|
def __init__(self, name: str, **kwargs):
|
|
19
|
-
"""
|
|
19
|
+
"""constructor
|
|
20
20
|
Args:
|
|
21
21
|
name (str): the handler name
|
|
22
22
|
"""
|
|
@@ -36,13 +36,16 @@ class GitlabHandler(APIHandler):
|
|
|
36
36
|
self._register_table("merge_requests", gitlab_merge_requests_data)
|
|
37
37
|
|
|
38
38
|
def connect(self) -> StatusResponse:
|
|
39
|
-
"""
|
|
39
|
+
"""Set up the connections required by the handler
|
|
40
40
|
Returns:
|
|
41
41
|
HandlerStatusResponse
|
|
42
42
|
"""
|
|
43
43
|
|
|
44
44
|
connection_kwargs = {}
|
|
45
45
|
|
|
46
|
+
if self.connection_data.get("url", None):
|
|
47
|
+
connection_kwargs["url"] = self.connection_data["url"]
|
|
48
|
+
|
|
46
49
|
if self.connection_data.get("api_key", None):
|
|
47
50
|
connection_kwargs["private_token"] = self.connection_data["api_key"]
|
|
48
51
|
|