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.

Files changed (163) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +40 -29
  3. mindsdb/api/a2a/__init__.py +1 -1
  4. mindsdb/api/a2a/agent.py +16 -10
  5. mindsdb/api/a2a/common/server/server.py +7 -3
  6. mindsdb/api/a2a/common/server/task_manager.py +12 -5
  7. mindsdb/api/a2a/common/types.py +66 -0
  8. mindsdb/api/a2a/task_manager.py +65 -17
  9. mindsdb/api/common/middleware.py +10 -12
  10. mindsdb/api/executor/command_executor.py +51 -40
  11. mindsdb/api/executor/datahub/datanodes/datanode.py +2 -2
  12. mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +7 -13
  13. mindsdb/api/executor/datahub/datanodes/integration_datanode.py +101 -49
  14. mindsdb/api/executor/datahub/datanodes/project_datanode.py +8 -4
  15. mindsdb/api/executor/datahub/datanodes/system_tables.py +3 -2
  16. mindsdb/api/executor/exceptions.py +29 -10
  17. mindsdb/api/executor/planner/plan_join.py +17 -3
  18. mindsdb/api/executor/planner/query_prepare.py +2 -20
  19. mindsdb/api/executor/sql_query/sql_query.py +74 -74
  20. mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +1 -2
  21. mindsdb/api/executor/sql_query/steps/subselect_step.py +0 -1
  22. mindsdb/api/executor/utilities/functions.py +6 -6
  23. mindsdb/api/executor/utilities/sql.py +37 -20
  24. mindsdb/api/http/gui.py +5 -11
  25. mindsdb/api/http/initialize.py +75 -61
  26. mindsdb/api/http/namespaces/agents.py +10 -15
  27. mindsdb/api/http/namespaces/analysis.py +13 -20
  28. mindsdb/api/http/namespaces/auth.py +1 -1
  29. mindsdb/api/http/namespaces/chatbots.py +0 -5
  30. mindsdb/api/http/namespaces/config.py +15 -11
  31. mindsdb/api/http/namespaces/databases.py +140 -201
  32. mindsdb/api/http/namespaces/file.py +17 -4
  33. mindsdb/api/http/namespaces/handlers.py +17 -7
  34. mindsdb/api/http/namespaces/knowledge_bases.py +28 -7
  35. mindsdb/api/http/namespaces/models.py +94 -126
  36. mindsdb/api/http/namespaces/projects.py +13 -22
  37. mindsdb/api/http/namespaces/sql.py +33 -25
  38. mindsdb/api/http/namespaces/tab.py +27 -37
  39. mindsdb/api/http/namespaces/views.py +1 -1
  40. mindsdb/api/http/start.py +16 -10
  41. mindsdb/api/mcp/__init__.py +2 -1
  42. mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +15 -20
  43. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +26 -50
  44. mindsdb/api/mysql/mysql_proxy/utilities/__init__.py +0 -1
  45. mindsdb/api/mysql/mysql_proxy/utilities/dump.py +8 -2
  46. mindsdb/integrations/handlers/byom_handler/byom_handler.py +165 -190
  47. mindsdb/integrations/handlers/databricks_handler/databricks_handler.py +98 -46
  48. mindsdb/integrations/handlers/druid_handler/druid_handler.py +32 -40
  49. mindsdb/integrations/handlers/file_handler/file_handler.py +7 -0
  50. mindsdb/integrations/handlers/gitlab_handler/gitlab_handler.py +5 -2
  51. mindsdb/integrations/handlers/lightwood_handler/functions.py +45 -79
  52. mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +438 -100
  53. mindsdb/integrations/handlers/mssql_handler/requirements_odbc.txt +3 -0
  54. mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +235 -3
  55. mindsdb/integrations/handlers/oracle_handler/__init__.py +2 -0
  56. mindsdb/integrations/handlers/oracle_handler/connection_args.py +7 -1
  57. mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +321 -16
  58. mindsdb/integrations/handlers/oracle_handler/requirements.txt +1 -1
  59. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +14 -2
  60. mindsdb/integrations/handlers/shopify_handler/shopify_handler.py +25 -12
  61. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +2 -1
  62. mindsdb/integrations/handlers/statsforecast_handler/requirements.txt +1 -0
  63. mindsdb/integrations/handlers/statsforecast_handler/requirements_extra.txt +1 -0
  64. mindsdb/integrations/handlers/web_handler/urlcrawl_helpers.py +4 -4
  65. mindsdb/integrations/handlers/zendesk_handler/zendesk_tables.py +144 -111
  66. mindsdb/integrations/libs/api_handler.py +10 -10
  67. mindsdb/integrations/libs/base.py +4 -4
  68. mindsdb/integrations/libs/llm/utils.py +2 -2
  69. mindsdb/integrations/libs/ml_handler_process/create_engine_process.py +4 -7
  70. mindsdb/integrations/libs/ml_handler_process/func_call_process.py +2 -7
  71. mindsdb/integrations/libs/ml_handler_process/learn_process.py +37 -47
  72. mindsdb/integrations/libs/ml_handler_process/update_engine_process.py +4 -7
  73. mindsdb/integrations/libs/ml_handler_process/update_process.py +2 -7
  74. mindsdb/integrations/libs/process_cache.py +132 -140
  75. mindsdb/integrations/libs/response.py +18 -12
  76. mindsdb/integrations/libs/vectordatabase_handler.py +26 -0
  77. mindsdb/integrations/utilities/files/file_reader.py +6 -7
  78. mindsdb/integrations/utilities/handlers/auth_utilities/snowflake/__init__.py +1 -0
  79. mindsdb/integrations/utilities/handlers/auth_utilities/snowflake/snowflake_jwt_gen.py +151 -0
  80. mindsdb/integrations/utilities/rag/config_loader.py +37 -26
  81. mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +83 -30
  82. mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +4 -4
  83. mindsdb/integrations/utilities/rag/retrievers/sql_retriever.py +55 -133
  84. mindsdb/integrations/utilities/rag/settings.py +58 -133
  85. mindsdb/integrations/utilities/rag/splitters/file_splitter.py +5 -15
  86. mindsdb/interfaces/agents/agents_controller.py +2 -3
  87. mindsdb/interfaces/agents/constants.py +0 -2
  88. mindsdb/interfaces/agents/litellm_server.py +34 -58
  89. mindsdb/interfaces/agents/mcp_client_agent.py +10 -10
  90. mindsdb/interfaces/agents/mindsdb_database_agent.py +5 -5
  91. mindsdb/interfaces/agents/run_mcp_agent.py +12 -21
  92. mindsdb/interfaces/chatbot/chatbot_task.py +20 -23
  93. mindsdb/interfaces/chatbot/polling.py +30 -18
  94. mindsdb/interfaces/data_catalog/data_catalog_loader.py +16 -17
  95. mindsdb/interfaces/data_catalog/data_catalog_reader.py +15 -4
  96. mindsdb/interfaces/database/data_handlers_cache.py +190 -0
  97. mindsdb/interfaces/database/database.py +3 -3
  98. mindsdb/interfaces/database/integrations.py +7 -110
  99. mindsdb/interfaces/database/projects.py +2 -6
  100. mindsdb/interfaces/database/views.py +1 -4
  101. mindsdb/interfaces/file/file_controller.py +6 -6
  102. mindsdb/interfaces/functions/controller.py +1 -1
  103. mindsdb/interfaces/functions/to_markdown.py +2 -2
  104. mindsdb/interfaces/jobs/jobs_controller.py +5 -9
  105. mindsdb/interfaces/jobs/scheduler.py +3 -9
  106. mindsdb/interfaces/knowledge_base/controller.py +244 -128
  107. mindsdb/interfaces/knowledge_base/evaluate.py +36 -41
  108. mindsdb/interfaces/knowledge_base/executor.py +11 -0
  109. mindsdb/interfaces/knowledge_base/llm_client.py +51 -17
  110. mindsdb/interfaces/knowledge_base/preprocessing/json_chunker.py +40 -61
  111. mindsdb/interfaces/model/model_controller.py +172 -168
  112. mindsdb/interfaces/query_context/context_controller.py +14 -2
  113. mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +10 -14
  114. mindsdb/interfaces/skills/retrieval_tool.py +43 -50
  115. mindsdb/interfaces/skills/skill_tool.py +2 -2
  116. mindsdb/interfaces/skills/skills_controller.py +1 -4
  117. mindsdb/interfaces/skills/sql_agent.py +25 -19
  118. mindsdb/interfaces/storage/db.py +16 -6
  119. mindsdb/interfaces/storage/fs.py +114 -169
  120. mindsdb/interfaces/storage/json.py +19 -18
  121. mindsdb/interfaces/tabs/tabs_controller.py +49 -72
  122. mindsdb/interfaces/tasks/task_monitor.py +3 -9
  123. mindsdb/interfaces/tasks/task_thread.py +7 -9
  124. mindsdb/interfaces/triggers/trigger_task.py +7 -13
  125. mindsdb/interfaces/triggers/triggers_controller.py +47 -52
  126. mindsdb/migrations/migrate.py +16 -16
  127. mindsdb/utilities/api_status.py +58 -0
  128. mindsdb/utilities/config.py +68 -2
  129. mindsdb/utilities/exception.py +40 -1
  130. mindsdb/utilities/fs.py +0 -1
  131. mindsdb/utilities/hooks/profiling.py +17 -14
  132. mindsdb/utilities/json_encoder.py +24 -10
  133. mindsdb/utilities/langfuse.py +40 -45
  134. mindsdb/utilities/log.py +272 -0
  135. mindsdb/utilities/ml_task_queue/consumer.py +52 -58
  136. mindsdb/utilities/ml_task_queue/producer.py +26 -30
  137. mindsdb/utilities/render/sqlalchemy_render.py +22 -20
  138. mindsdb/utilities/starters.py +0 -10
  139. mindsdb/utilities/utils.py +2 -2
  140. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/METADATA +293 -276
  141. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/RECORD +144 -158
  142. mindsdb/api/mysql/mysql_proxy/utilities/exceptions.py +0 -14
  143. mindsdb/api/postgres/__init__.py +0 -0
  144. mindsdb/api/postgres/postgres_proxy/__init__.py +0 -0
  145. mindsdb/api/postgres/postgres_proxy/executor/__init__.py +0 -1
  146. mindsdb/api/postgres/postgres_proxy/executor/executor.py +0 -189
  147. mindsdb/api/postgres/postgres_proxy/postgres_packets/__init__.py +0 -0
  148. mindsdb/api/postgres/postgres_proxy/postgres_packets/errors.py +0 -322
  149. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_fields.py +0 -34
  150. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message.py +0 -31
  151. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_formats.py +0 -1265
  152. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_identifiers.py +0 -31
  153. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_packets.py +0 -253
  154. mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +0 -477
  155. mindsdb/api/postgres/postgres_proxy/utilities/__init__.py +0 -10
  156. mindsdb/api/postgres/start.py +0 -11
  157. mindsdb/integrations/handlers/mssql_handler/tests/__init__.py +0 -0
  158. mindsdb/integrations/handlers/mssql_handler/tests/test_mssql_handler.py +0 -169
  159. mindsdb/integrations/handlers/oracle_handler/tests/__init__.py +0 -0
  160. mindsdb/integrations/handlers/oracle_handler/tests/test_oracle_handler.py +0 -32
  161. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/WHEEL +0 -0
  162. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/licenses/LICENSE +0 -0
  163. {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
- key in self.connection_data
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'Request error when connecting to Databricks: {request_error}')
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'Runtime error when connecting to Databricks: {runtime_error}')
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'Unknown error when connecting to Databricks: {unknown_error}')
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 'schema' in self.connection_data:
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'The schema {self.connection_data["schema"]} does not exist!')
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'Connection check to Databricks failed, {known_error}!')
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'Connection check to Databricks failed due to an unknown error, {unknown_error}!')
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
- f'Server error running query: {query} on Databricks, {server_error}!'
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
- f'Unknown error running query: {query} on Databricks, {unknown_error}!'
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
- query = """
231
- SHOW TABLES;
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
- df = result.data_frame
236
- if df is not None:
237
- result.data_frame = df.rename(columns={"tableName": "table_name", "database": "schema_name"})
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: Text) -> Response:
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 (Text): The name of the table for which to retrieve column information.
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
- query = f"DESCRIBE TABLE {table_name};"
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.ast.base import ASTNode
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 = 'druid'
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 = 'druid'
40
+ self.dialect = "druid"
41
41
 
42
- optional_parameters = ['user', 'password']
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 'path' not in connection_data:
48
- connection_data['path'] = '/druid/v2/sql/'
47
+ if "path" not in connection_data:
48
+ connection_data["path"] = "/druid/v2/sql/"
49
49
 
50
- if 'scheme' not in connection_data:
51
- connection_data['scheme'] = 'http'
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['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']
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('select 1') # raise exception if provided wrong credentials
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'Error connecting to Druid, {e}!')
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'Error running query: {query} on Pinot!')
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" = 'druid' AND "TABLE_NAME" = '{table_name}'
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
- """ constructor
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
- """ Set up the connections required by the handler
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