MindsDB 25.9.2.0a1__py3-none-any.whl → 25.9.3rc1__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 (116) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +39 -20
  3. mindsdb/api/a2a/agent.py +7 -9
  4. mindsdb/api/a2a/common/server/server.py +3 -3
  5. mindsdb/api/a2a/common/server/task_manager.py +4 -4
  6. mindsdb/api/a2a/task_manager.py +15 -17
  7. mindsdb/api/common/middleware.py +9 -11
  8. mindsdb/api/executor/command_executor.py +2 -4
  9. mindsdb/api/executor/datahub/datanodes/datanode.py +2 -2
  10. mindsdb/api/executor/datahub/datanodes/integration_datanode.py +100 -48
  11. mindsdb/api/executor/datahub/datanodes/project_datanode.py +8 -4
  12. mindsdb/api/executor/datahub/datanodes/system_tables.py +1 -1
  13. mindsdb/api/executor/exceptions.py +29 -10
  14. mindsdb/api/executor/planner/plan_join.py +17 -3
  15. mindsdb/api/executor/sql_query/sql_query.py +74 -74
  16. mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +1 -2
  17. mindsdb/api/executor/sql_query/steps/subselect_step.py +0 -1
  18. mindsdb/api/executor/utilities/functions.py +6 -6
  19. mindsdb/api/executor/utilities/sql.py +32 -16
  20. mindsdb/api/http/gui.py +5 -11
  21. mindsdb/api/http/initialize.py +8 -10
  22. mindsdb/api/http/namespaces/agents.py +10 -12
  23. mindsdb/api/http/namespaces/analysis.py +13 -20
  24. mindsdb/api/http/namespaces/auth.py +1 -1
  25. mindsdb/api/http/namespaces/config.py +15 -11
  26. mindsdb/api/http/namespaces/databases.py +140 -201
  27. mindsdb/api/http/namespaces/file.py +15 -4
  28. mindsdb/api/http/namespaces/handlers.py +7 -2
  29. mindsdb/api/http/namespaces/knowledge_bases.py +8 -7
  30. mindsdb/api/http/namespaces/models.py +94 -126
  31. mindsdb/api/http/namespaces/projects.py +13 -22
  32. mindsdb/api/http/namespaces/sql.py +33 -25
  33. mindsdb/api/http/namespaces/tab.py +27 -37
  34. mindsdb/api/http/namespaces/views.py +1 -1
  35. mindsdb/api/http/start.py +14 -8
  36. mindsdb/api/mcp/__init__.py +2 -1
  37. mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +15 -20
  38. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +26 -50
  39. mindsdb/api/mysql/mysql_proxy/utilities/__init__.py +0 -1
  40. mindsdb/api/postgres/postgres_proxy/executor/executor.py +6 -13
  41. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_packets.py +40 -28
  42. mindsdb/integrations/handlers/byom_handler/byom_handler.py +168 -185
  43. mindsdb/integrations/handlers/file_handler/file_handler.py +7 -0
  44. mindsdb/integrations/handlers/lightwood_handler/functions.py +45 -79
  45. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +13 -1
  46. mindsdb/integrations/handlers/shopify_handler/shopify_handler.py +25 -12
  47. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +2 -1
  48. mindsdb/integrations/handlers/statsforecast_handler/requirements.txt +1 -0
  49. mindsdb/integrations/handlers/statsforecast_handler/requirements_extra.txt +1 -0
  50. mindsdb/integrations/handlers/web_handler/urlcrawl_helpers.py +4 -4
  51. mindsdb/integrations/libs/api_handler.py +10 -10
  52. mindsdb/integrations/libs/base.py +4 -4
  53. mindsdb/integrations/libs/llm/utils.py +2 -2
  54. mindsdb/integrations/libs/ml_handler_process/create_engine_process.py +4 -7
  55. mindsdb/integrations/libs/ml_handler_process/func_call_process.py +2 -7
  56. mindsdb/integrations/libs/ml_handler_process/learn_process.py +37 -47
  57. mindsdb/integrations/libs/ml_handler_process/update_engine_process.py +4 -7
  58. mindsdb/integrations/libs/ml_handler_process/update_process.py +2 -7
  59. mindsdb/integrations/libs/process_cache.py +132 -140
  60. mindsdb/integrations/libs/response.py +18 -12
  61. mindsdb/integrations/libs/vectordatabase_handler.py +26 -0
  62. mindsdb/integrations/utilities/files/file_reader.py +6 -7
  63. mindsdb/integrations/utilities/rag/config_loader.py +37 -26
  64. mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +59 -9
  65. mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +4 -4
  66. mindsdb/integrations/utilities/rag/retrievers/sql_retriever.py +55 -133
  67. mindsdb/integrations/utilities/rag/settings.py +58 -133
  68. mindsdb/integrations/utilities/rag/splitters/file_splitter.py +5 -15
  69. mindsdb/interfaces/agents/agents_controller.py +2 -1
  70. mindsdb/interfaces/agents/constants.py +0 -2
  71. mindsdb/interfaces/agents/litellm_server.py +34 -58
  72. mindsdb/interfaces/agents/mcp_client_agent.py +10 -10
  73. mindsdb/interfaces/agents/mindsdb_database_agent.py +5 -5
  74. mindsdb/interfaces/agents/run_mcp_agent.py +12 -21
  75. mindsdb/interfaces/chatbot/chatbot_task.py +20 -23
  76. mindsdb/interfaces/chatbot/polling.py +30 -18
  77. mindsdb/interfaces/data_catalog/data_catalog_loader.py +10 -10
  78. mindsdb/interfaces/database/integrations.py +19 -2
  79. mindsdb/interfaces/file/file_controller.py +6 -6
  80. mindsdb/interfaces/functions/controller.py +1 -1
  81. mindsdb/interfaces/functions/to_markdown.py +2 -2
  82. mindsdb/interfaces/jobs/jobs_controller.py +5 -5
  83. mindsdb/interfaces/jobs/scheduler.py +3 -8
  84. mindsdb/interfaces/knowledge_base/controller.py +50 -23
  85. mindsdb/interfaces/knowledge_base/preprocessing/json_chunker.py +40 -61
  86. mindsdb/interfaces/model/model_controller.py +170 -166
  87. mindsdb/interfaces/query_context/context_controller.py +14 -2
  88. mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +6 -4
  89. mindsdb/interfaces/skills/retrieval_tool.py +43 -50
  90. mindsdb/interfaces/skills/skill_tool.py +2 -2
  91. mindsdb/interfaces/skills/sql_agent.py +25 -19
  92. mindsdb/interfaces/storage/fs.py +114 -169
  93. mindsdb/interfaces/storage/json.py +19 -18
  94. mindsdb/interfaces/tabs/tabs_controller.py +49 -72
  95. mindsdb/interfaces/tasks/task_monitor.py +3 -9
  96. mindsdb/interfaces/tasks/task_thread.py +7 -9
  97. mindsdb/interfaces/triggers/trigger_task.py +7 -13
  98. mindsdb/interfaces/triggers/triggers_controller.py +47 -50
  99. mindsdb/migrations/migrate.py +16 -16
  100. mindsdb/utilities/api_status.py +58 -0
  101. mindsdb/utilities/config.py +49 -0
  102. mindsdb/utilities/exception.py +40 -1
  103. mindsdb/utilities/fs.py +0 -1
  104. mindsdb/utilities/hooks/profiling.py +17 -14
  105. mindsdb/utilities/langfuse.py +40 -45
  106. mindsdb/utilities/log.py +272 -0
  107. mindsdb/utilities/ml_task_queue/consumer.py +52 -58
  108. mindsdb/utilities/ml_task_queue/producer.py +26 -30
  109. mindsdb/utilities/render/sqlalchemy_render.py +7 -6
  110. mindsdb/utilities/utils.py +2 -2
  111. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.9.3rc1.dist-info}/METADATA +269 -264
  112. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.9.3rc1.dist-info}/RECORD +115 -115
  113. mindsdb/api/mysql/mysql_proxy/utilities/exceptions.py +0 -14
  114. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.9.3rc1.dist-info}/WHEEL +0 -0
  115. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.9.3rc1.dist-info}/licenses/LICENSE +0 -0
  116. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.9.3rc1.dist-info}/top_level.txt +0 -0
@@ -19,74 +19,69 @@ from mindsdb_sql_parser import parse_sql, ParsingException
19
19
  from mindsdb_sql_parser.ast import CreateTable, DropTables
20
20
  from mindsdb.utilities.exception import EntityNotExistsError
21
21
  from mindsdb.integrations.libs.response import HandlerStatusResponse
22
+ from mindsdb.utilities import log
22
23
 
23
24
 
24
- @ns_conf.route('/')
25
+ logger = log.getLogger(__name__)
26
+
27
+
28
+ @ns_conf.route("/")
25
29
  class DatabasesResource(Resource):
26
- @ns_conf.doc('list_databases')
27
- @api_endpoint_metrics('GET', '/databases')
30
+ @ns_conf.doc("list_databases")
31
+ @api_endpoint_metrics("GET", "/databases")
28
32
  def get(self):
29
- '''List all databases'''
33
+ """List all databases"""
30
34
  session = SessionController()
31
35
  return session.database_controller.get_list()
32
36
 
33
- @ns_conf.doc('create_database')
34
- @api_endpoint_metrics('POST', '/databases')
37
+ @ns_conf.doc("create_database")
38
+ @api_endpoint_metrics("POST", "/databases")
35
39
  def post(self):
36
- '''Create a database'''
37
- if 'database' not in request.json:
40
+ """Create a database"""
41
+ if "database" not in request.json:
38
42
  return http_error(
39
- HTTPStatus.BAD_REQUEST, 'Wrong argument',
40
- 'Must provide "database" parameter in POST body'
43
+ HTTPStatus.BAD_REQUEST, "Wrong argument", 'Must provide "database" parameter in POST body'
41
44
  )
42
- check_connection = request.json.get('check_connection', False)
45
+ check_connection = request.json.get("check_connection", False)
43
46
  session = SessionController()
44
- database = request.json['database']
47
+ database = request.json["database"]
45
48
  parameters = {}
46
- if 'name' not in database:
47
- return http_error(
48
- HTTPStatus.BAD_REQUEST, 'Wrong argument',
49
- 'Missing "name" field for database'
50
- )
51
- if 'engine' not in database:
49
+ if "name" not in database:
50
+ return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Missing "name" field for database')
51
+ if "engine" not in database:
52
52
  return http_error(
53
- HTTPStatus.BAD_REQUEST, 'Wrong argument',
54
- 'Missing "engine" field for database. If you want to create a project instead, use the /api/projects endpoint.'
53
+ HTTPStatus.BAD_REQUEST,
54
+ "Wrong argument",
55
+ 'Missing "engine" field for database. If you want to create a project instead, use the /api/projects endpoint.',
55
56
  )
56
- if 'parameters' in database:
57
- parameters = database['parameters']
58
- name = database['name']
57
+ if "parameters" in database:
58
+ parameters = database["parameters"]
59
+ name = database["name"]
59
60
 
60
61
  if session.database_controller.exists(name):
61
- return http_error(
62
- HTTPStatus.CONFLICT, 'Name conflict',
63
- f'Database with name {name} already exists.'
64
- )
62
+ return http_error(HTTPStatus.CONFLICT, "Name conflict", f"Database with name {name} already exists.")
65
63
 
66
64
  storage = None
67
65
  if check_connection:
68
66
  try:
69
- handler = session.integration_controller.create_tmp_handler(name, database['engine'], parameters)
67
+ handler = session.integration_controller.create_tmp_handler(name, database["engine"], parameters)
70
68
  status = handler.check_connection()
71
69
  except ImportError as import_error:
72
70
  status = HandlerStatusResponse(success=False, error_message=str(import_error))
73
71
 
74
72
  if status.success is not True:
75
- if hasattr(status, 'redirect_url') and isinstance(status, str):
73
+ if hasattr(status, "redirect_url") and isinstance(status, str):
76
74
  return {
77
75
  "status": "redirect_required",
78
76
  "redirect_url": status.redirect_url,
79
- "detail": status.error_message
77
+ "detail": status.error_message,
80
78
  }, HTTPStatus.OK
81
- return {
82
- "status": "connection_error",
83
- "detail": status.error_message
84
- }, HTTPStatus.OK
79
+ return {"status": "connection_error", "detail": status.error_message}, HTTPStatus.OK
85
80
 
86
81
  if status.copy_storage:
87
82
  storage = handler.handler_storage.export_files()
88
83
 
89
- new_integration_id = session.integration_controller.add(name, database['engine'], parameters)
84
+ new_integration_id = session.integration_controller.add(name, database["engine"], parameters)
90
85
 
91
86
  if storage:
92
87
  handler = session.integration_controller.get_data_handler(name, connect=False)
@@ -96,38 +91,35 @@ class DatabasesResource(Resource):
96
91
  return new_integration, HTTPStatus.CREATED
97
92
 
98
93
 
99
- @ns_conf.route('/status')
94
+ @ns_conf.route("/status")
100
95
  class DatabasesStatusResource(Resource):
101
- @ns_conf.doc('check_database_connection_status')
102
- @api_endpoint_metrics('POST', '/databases/status')
96
+ @ns_conf.doc("check_database_connection_status")
97
+ @api_endpoint_metrics("POST", "/databases/status")
103
98
  def post(self):
104
- '''Check the connection parameters for a database'''
99
+ """Check the connection parameters for a database"""
105
100
  data = {}
106
- if request.content_type == 'application/json':
101
+ if request.content_type == "application/json":
107
102
  data.update(request.json or {})
108
- elif request.content_type.startswith('multipart/form-data'):
103
+ elif request.content_type.startswith("multipart/form-data"):
109
104
  data.update(request.form or {})
110
105
 
111
- if 'engine' not in data:
112
- return http_error(
113
- HTTPStatus.BAD_REQUEST, 'Wrong argument',
114
- 'Missing "engine" field for database'
115
- )
106
+ if "engine" not in data:
107
+ return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Missing "engine" field for database')
116
108
 
117
- engine = data['engine']
109
+ engine = data["engine"]
118
110
  parameters = data
119
- del parameters['engine']
111
+ del parameters["engine"]
120
112
 
121
113
  files = request.files
122
114
  temp_dir = None
123
115
  if files is not None and len(files) > 0:
124
- temp_dir = tempfile.mkdtemp(prefix='integration_files_')
116
+ temp_dir = tempfile.mkdtemp(prefix="integration_files_")
125
117
  for key, file in files.items():
126
118
  temp_dir_path = Path(temp_dir)
127
119
  file_name = Path(file.filename)
128
120
  file_path = temp_dir_path.joinpath(file_name).resolve()
129
121
  if temp_dir_path not in file_path.parents:
130
- raise Exception(f'Can not save file at path: {file_path}')
122
+ raise Exception(f"Can not save file at path: {file_path}")
131
123
  file.save(file_path)
132
124
  parameters[key] = str(file_path)
133
125
 
@@ -145,102 +137,87 @@ class DatabasesStatusResource(Resource):
145
137
  shutil.rmtree(temp_dir)
146
138
 
147
139
  if not status.success:
148
- if hasattr(status, 'redirect_url') and isinstance(status, str):
140
+ if hasattr(status, "redirect_url") and isinstance(status, str):
149
141
  return {
150
142
  "status": "redirect_required",
151
143
  "redirect_url": status.redirect_url,
152
- "detail": status.error_message
144
+ "detail": status.error_message,
153
145
  }, HTTPStatus.OK
154
- return {
155
- "status": "connection_error",
156
- "detail": status.error_message
157
- }, HTTPStatus.OK
146
+ return {"status": "connection_error", "detail": status.error_message}, HTTPStatus.OK
158
147
 
159
148
  return {
160
149
  "status": "success",
161
150
  }, HTTPStatus.OK
162
151
 
163
152
 
164
- @ns_conf.route('/<database_name>')
153
+ @ns_conf.route("/<database_name>")
165
154
  class DatabaseResource(Resource):
166
- @ns_conf.doc('get_database')
167
- @api_endpoint_metrics('GET', '/databases/database')
155
+ @ns_conf.doc("get_database")
156
+ @api_endpoint_metrics("GET", "/databases/database")
168
157
  def get(self, database_name):
169
- '''Gets a database by name'''
158
+ """Gets a database by name"""
170
159
  session = SessionController()
171
- check_connection = request.args.get('check_connection', 'false').lower() in ('1', 'true')
160
+ check_connection = request.args.get("check_connection", "false").lower() in ("1", "true")
172
161
  try:
173
162
  project = session.database_controller.get_project(database_name)
174
- result = {
175
- 'name': database_name,
176
- 'type': 'project',
177
- 'id': project.id,
178
- 'engine': None
179
- }
163
+ result = {"name": database_name, "type": "project", "id": project.id, "engine": None}
180
164
  if check_connection:
181
- result['connection_status'] = {
182
- 'success': True,
183
- 'error_message': None
184
- }
165
+ result["connection_status"] = {"success": True, "error_message": None}
185
166
  except (ValueError, EntityNotExistsError):
186
167
  integration = session.integration_controller.get(database_name)
187
168
  if integration is None:
188
169
  return http_error(
189
- HTTPStatus.NOT_FOUND, 'Database not found',
190
- f'Database with name {database_name} does not exist.'
170
+ HTTPStatus.NOT_FOUND, "Database not found", f"Database with name {database_name} does not exist."
191
171
  )
192
172
  result = integration
193
173
  if check_connection:
194
- integration['connection_status'] = {
195
- 'success': False,
196
- 'error_message': None
197
- }
174
+ integration["connection_status"] = {"success": False, "error_message": None}
198
175
  try:
199
176
  handler = session.integration_controller.get_data_handler(database_name)
200
177
  status = handler.check_connection()
201
- integration['connection_status']['success'] = status.success
202
- integration['connection_status']['error_message'] = status.error_message
178
+ integration["connection_status"]["success"] = status.success
179
+ integration["connection_status"]["error_message"] = status.error_message
203
180
  except Exception as e:
204
- integration['connection_status']['success'] = False
205
- integration['connection_status']['error_message'] = str(e)
181
+ integration["connection_status"]["success"] = False
182
+ integration["connection_status"]["error_message"] = str(e)
206
183
 
207
184
  return result
208
185
 
209
- @ns_conf.doc('update_database')
210
- @api_endpoint_metrics('PUT', '/databases/database')
186
+ @ns_conf.doc("update_database")
187
+ @api_endpoint_metrics("PUT", "/databases/database")
211
188
  def put(self, database_name):
212
- '''Updates or creates a database'''
213
- if 'database' not in request.json:
189
+ """Updates or creates a database"""
190
+ if "database" not in request.json:
214
191
  return http_error(
215
- HTTPStatus.BAD_REQUEST, 'Wrong argument',
216
- 'Must provide "database" parameter in POST body'
192
+ HTTPStatus.BAD_REQUEST, "Wrong argument", 'Must provide "database" parameter in POST body'
217
193
  )
218
194
 
219
195
  session = SessionController()
220
196
  parameters = {}
221
- database = request.json['database']
222
- check_connection = request.json.get('check_connection', False)
197
+ database = request.json["database"]
198
+ check_connection = request.json.get("check_connection", False)
223
199
 
224
- if 'parameters' in database:
225
- parameters = database['parameters']
200
+ if "parameters" in database:
201
+ parameters = database["parameters"]
226
202
  if not session.database_controller.exists(database_name):
227
203
  # Create.
228
- if 'engine' not in database:
204
+ if "engine" not in database:
229
205
  return http_error(
230
- HTTPStatus.BAD_REQUEST, 'Wrong argument',
206
+ HTTPStatus.BAD_REQUEST,
207
+ "Wrong argument",
231
208
  'Missing "engine" field for new database. '
232
- 'If you want to create a project instead, use the POST /api/projects endpoint.'
209
+ "If you want to create a project instead, use the POST /api/projects endpoint.",
233
210
  )
234
- new_integration_id = session.integration_controller.add(database_name, database['engine'], parameters)
211
+ new_integration_id = session.integration_controller.add(database_name, database["engine"], parameters)
235
212
  new_integration = session.database_controller.get_integration(new_integration_id)
236
213
  return new_integration, HTTPStatus.CREATED
237
214
 
238
215
  if check_connection:
239
216
  existing_integration = session.integration_controller.get(database_name)
240
- temp_name = f'{database_name}_{time.time()}'.replace('.', '')
217
+ temp_name = f"{database_name}_{time.time()}".replace(".", "")
241
218
  try:
242
219
  handler = session.integration_controller.create_tmp_handler(
243
- temp_name, existing_integration['engine'], parameters
220
+ temp_name, existing_integration["engine"], parameters
244
221
  )
245
222
  status = handler.check_connection()
246
223
  except ImportError as import_error:
@@ -248,94 +225,81 @@ class DatabaseResource(Resource):
248
225
 
249
226
  if status.success is not True:
250
227
  return http_error(
251
- HTTPStatus.BAD_REQUEST, 'Connection error',
252
- status.error_message or 'Connection error'
228
+ HTTPStatus.BAD_REQUEST, "Connection error", status.error_message or "Connection error"
253
229
  )
254
230
 
255
231
  session.integration_controller.modify(database_name, parameters)
256
232
  return session.integration_controller.get(database_name)
257
233
 
258
- @ns_conf.doc('delete_database')
259
- @api_endpoint_metrics('DELETE', '/databases/database')
234
+ @ns_conf.doc("delete_database")
235
+ @api_endpoint_metrics("DELETE", "/databases/database")
260
236
  def delete(self, database_name):
261
- '''Deletes a database by name'''
237
+ """Deletes a database by name"""
262
238
  session = SessionController()
263
239
  if not session.database_controller.exists(database_name):
264
240
  return http_error(
265
- HTTPStatus.NOT_FOUND, 'Database not found',
266
- f'Database with name {database_name} does not exist.'
241
+ HTTPStatus.NOT_FOUND, "Database not found", f"Database with name {database_name} does not exist."
267
242
  )
268
243
  try:
269
244
  session.database_controller.delete(database_name)
270
245
  except Exception as e:
246
+ logger.debug(f"Error while deleting database '{database_name}'", exc_info=True)
271
247
  return http_error(
272
- HTTPStatus.BAD_REQUEST, 'Error',
273
- f'Cannot delete database {database_name}. '
274
- + 'This is most likely a system database, a permanent integration, or an ML engine with active models. '
275
- + f'Full error: {e}. '
276
- + 'Please check the name and try again.'
248
+ HTTPStatus.BAD_REQUEST,
249
+ "Error",
250
+ f"Cannot delete database {database_name}. "
251
+ + "This is most likely a system database, a permanent integration, or an ML engine with active models. "
252
+ + f"Full error: {e}. "
253
+ + "Please check the name and try again.",
277
254
  )
278
- return '', HTTPStatus.NO_CONTENT
255
+ return "", HTTPStatus.NO_CONTENT
279
256
 
280
257
 
281
258
  def _tables_row_to_obj(table_row: TablesRow) -> Dict:
282
259
  type = table_row.TABLE_TYPE.lower()
283
- if table_row.TABLE_TYPE == 'BASE TABLE':
284
- type = 'data'
285
- return {
286
- 'name': table_row.TABLE_NAME,
287
- 'type': type
288
- }
260
+ if table_row.TABLE_TYPE == "BASE TABLE":
261
+ type = "data"
262
+ return {"name": table_row.TABLE_NAME, "type": type}
289
263
 
290
264
 
291
- @ns_conf.route('/<database_name>/tables')
265
+ @ns_conf.route("/<database_name>/tables")
292
266
  class TablesList(Resource):
293
- @ns_conf.doc('list_tables')
294
- @api_endpoint_metrics('GET', '/databases/database/tables')
267
+ @ns_conf.doc("list_tables")
268
+ @api_endpoint_metrics("GET", "/databases/database/tables")
295
269
  def get(self, database_name):
296
- '''Get all tables in a database'''
270
+ """Get all tables in a database"""
297
271
  session = SessionController()
298
272
  datanode = session.datahub.get(database_name)
299
273
  all_tables = datanode.get_tables()
300
274
  table_objs = [_tables_row_to_obj(t) for t in all_tables]
301
275
  return table_objs
302
276
 
303
- @ns_conf.doc('create_table')
304
- @api_endpoint_metrics('POST', '/databases/database/tables')
277
+ @ns_conf.doc("create_table")
278
+ @api_endpoint_metrics("POST", "/databases/database/tables")
305
279
  def post(self, database_name):
306
- '''Creates a table in a database'''
307
- if 'table' not in request.json:
308
- return http_error(
309
- HTTPStatus.BAD_REQUEST, 'Wrong argument',
310
- 'Must provide "table" parameter in POST body'
311
- )
312
- table = request.json['table']
313
- if 'name' not in table:
314
- return http_error(
315
- HTTPStatus.BAD_REQUEST, 'Wrong argument',
316
- 'Missing "name" field for table'
317
- )
318
- if 'select' not in table:
319
- return http_error(
320
- HTTPStatus.BAD_REQUEST, 'Wrong argument',
321
- 'Missing "select" field for table'
322
- )
323
- table_name = table['name']
324
- select_query = table['select']
280
+ """Creates a table in a database"""
281
+ if "table" not in request.json:
282
+ return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Must provide "table" parameter in POST body')
283
+ table = request.json["table"]
284
+ if "name" not in table:
285
+ return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Missing "name" field for table')
286
+ if "select" not in table:
287
+ return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Missing "select" field for table')
288
+ table_name = table["name"]
289
+ select_query = table["select"]
325
290
  replace = False
326
- if 'replace' in table:
327
- replace = table['replace']
291
+ if "replace" in table:
292
+ replace = table["replace"]
328
293
 
329
294
  session = SessionController()
330
295
  try:
331
296
  session.database_controller.get_project(database_name)
332
- error_message = f'Database {database_name} is a project. ' \
333
- + f'If you want to create a model or view, use the projects/{database_name}/models/{table_name} or ' \
334
- + f'projects/{database_name}/views/{table_name} endpoints instead.'
335
- return http_error(
336
- HTTPStatus.BAD_REQUEST, 'Error',
337
- error_message
297
+ error_message = (
298
+ f"Database {database_name} is a project. "
299
+ + f"If you want to create a model or view, use the projects/{database_name}/models/{table_name} or "
300
+ + f"projects/{database_name}/views/{table_name} endpoints instead."
338
301
  )
302
+ return http_error(HTTPStatus.BAD_REQUEST, "Error", error_message)
339
303
  except EntityNotExistsError:
340
304
  # Only support creating tables from integrations.
341
305
  pass
@@ -343,40 +307,26 @@ class TablesList(Resource):
343
307
  datanode = session.datahub.get(database_name)
344
308
  if datanode is None:
345
309
  return http_error(
346
- HTTPStatus.NOT_FOUND, 'Database not exists',
347
- f'Database with name {database_name} does not exist'
310
+ HTTPStatus.NOT_FOUND, "Database not exists", f"Database with name {database_name} does not exist"
348
311
  )
349
312
  all_tables = datanode.get_tables()
350
313
  for t in all_tables:
351
314
  if t.TABLE_NAME == table_name and not replace:
352
- return http_error(
353
- HTTPStatus.CONFLICT, 'Name conflict',
354
- f'Table with name {table_name} already exists'
355
- )
315
+ return http_error(HTTPStatus.CONFLICT, "Name conflict", f"Table with name {table_name} already exists")
356
316
 
357
317
  try:
358
318
  select_ast = parse_sql(select_query)
359
319
  except ParsingException:
360
- return http_error(
361
- HTTPStatus.BAD_REQUEST, 'Error',
362
- f'Could not parse select statement {select_query}'
363
- )
320
+ return http_error(HTTPStatus.BAD_REQUEST, "Error", f"Could not parse select statement {select_query}")
364
321
 
365
- create_ast = CreateTable(
366
- f'{database_name}.{table_name}',
367
- from_select=select_ast,
368
- is_replace=replace
369
- )
322
+ create_ast = CreateTable(f"{database_name}.{table_name}", from_select=select_ast, is_replace=replace)
370
323
 
371
324
  mysql_proxy = FakeMysqlProxy()
372
325
 
373
326
  try:
374
327
  mysql_proxy.process_query(create_ast.get_string())
375
328
  except Exception as e:
376
- return http_error(
377
- HTTPStatus.BAD_REQUEST, 'Error',
378
- str(e)
379
- )
329
+ return http_error(HTTPStatus.BAD_REQUEST, "Error", str(e))
380
330
 
381
331
  all_tables = datanode.get_tables()
382
332
  try:
@@ -384,17 +334,16 @@ class TablesList(Resource):
384
334
  return _tables_row_to_obj(matching_table), HTTPStatus.CREATED
385
335
  except StopIteration:
386
336
  return http_error(
387
- HTTPStatus.INTERNAL_SERVER_ERROR, 'Error',
388
- f'Table with name {table_name} could not be created'
337
+ HTTPStatus.INTERNAL_SERVER_ERROR, "Error", f"Table with name {table_name} could not be created"
389
338
  )
390
339
 
391
340
 
392
- @ns_conf.route('/<database_name>/tables/<table_name>')
393
- @ns_conf.param('database_name', 'Name of the database')
394
- @ns_conf.param('table_name', 'Name of the table')
341
+ @ns_conf.route("/<database_name>/tables/<table_name>")
342
+ @ns_conf.param("database_name", "Name of the database")
343
+ @ns_conf.param("table_name", "Name of the table")
395
344
  class TableResource(Resource):
396
- @ns_conf.doc('get_table')
397
- @api_endpoint_metrics('GET', '/databases/database/tables/table')
345
+ @ns_conf.doc("get_table")
346
+ @api_endpoint_metrics("GET", "/databases/database/tables/table")
398
347
  def get(self, database_name, table_name):
399
348
  session = SessionController()
400
349
  datanode = session.datahub.get(database_name)
@@ -403,21 +352,20 @@ class TableResource(Resource):
403
352
  matching_table = next(t for t in all_tables if t.TABLE_NAME == table_name)
404
353
  return _tables_row_to_obj(matching_table)
405
354
  except StopIteration:
406
- return http_error(
407
- HTTPStatus.NOT_FOUND, 'Table not found',
408
- f'Table with name {table_name} not found'
409
- )
355
+ return http_error(HTTPStatus.NOT_FOUND, "Table not found", f"Table with name {table_name} not found")
410
356
 
411
- @ns_conf.doc('drop_table')
412
- @api_endpoint_metrics('DELETE', '/databases/database/tables/table')
357
+ @ns_conf.doc("drop_table")
358
+ @api_endpoint_metrics("DELETE", "/databases/database/tables/table")
413
359
  def delete(self, database_name, table_name):
414
360
  session = SessionController()
415
361
  try:
416
362
  session.database_controller.get_project(database_name)
417
- error_message = f'Database {database_name} is a project. ' \
418
- + f'If you want to delete a model or view, use the projects/{database_name}/models/{table_name} or ' \
419
- + f'projects/{database_name}/views/{table_name} endpoints instead.'
420
- return http_error(HTTPStatus.BAD_REQUEST, 'Error', error_message)
363
+ error_message = (
364
+ f"Database {database_name} is a project. "
365
+ + f"If you want to delete a model or view, use the projects/{database_name}/models/{table_name} or "
366
+ + f"projects/{database_name}/views/{table_name} endpoints instead."
367
+ )
368
+ return http_error(HTTPStatus.BAD_REQUEST, "Error", error_message)
421
369
  except EntityNotExistsError:
422
370
  # Only support dropping tables from integrations.
423
371
  pass
@@ -425,37 +373,28 @@ class TableResource(Resource):
425
373
  datanode = session.datahub.get(database_name)
426
374
  if datanode is None:
427
375
  return http_error(
428
- HTTPStatus.NOT_FOUND, 'Database not found',
429
- f'Database with name {database_name} not found'
376
+ HTTPStatus.NOT_FOUND, "Database not found", f"Database with name {database_name} not found"
430
377
  )
431
378
  all_tables = datanode.get_tables()
432
379
  try:
433
380
  next(t for t in all_tables if t.TABLE_NAME == table_name)
434
381
  except StopIteration:
435
- return http_error(
436
- HTTPStatus.NOT_FOUND, 'Table not found',
437
- f'Table with name {table_name} not found'
438
- )
382
+ return http_error(HTTPStatus.NOT_FOUND, "Table not found", f"Table with name {table_name} not found")
439
383
 
440
- drop_ast = DropTables(
441
- tables=[table_name],
442
- if_exists=True
443
- )
384
+ drop_ast = DropTables(tables=[table_name], if_exists=True)
444
385
 
445
386
  try:
446
387
  integration_handler = session.integration_controller.get_data_handler(database_name)
447
388
  except Exception:
448
389
  return http_error(
449
- HTTPStatus.INTERNAL_SERVER_ERROR, 'Error',
450
- f'Could not get database handler for {database_name}'
390
+ HTTPStatus.INTERNAL_SERVER_ERROR, "Error", f"Could not get database handler for {database_name}"
451
391
  )
452
392
  try:
453
393
  result = integration_handler.query(drop_ast)
454
394
  except NotImplementedError:
455
395
  return http_error(
456
- HTTPStatus.BAD_REQUEST, 'Error',
457
- f'Database {database_name} does not support dropping tables.'
396
+ HTTPStatus.BAD_REQUEST, "Error", f"Database {database_name} does not support dropping tables."
458
397
  )
459
398
  if result.type == RESPONSE_TYPE.ERROR:
460
- return http_error(HTTPStatus.BAD_REQUEST, 'Error', result.error_message)
461
- return '', HTTPStatus.NO_CONTENT
399
+ return http_error(HTTPStatus.BAD_REQUEST, "Error", result.error_message)
400
+ return "", HTTPStatus.NO_CONTENT
@@ -160,7 +160,11 @@ class File(Resource):
160
160
  "Сan't determine remote file size",
161
161
  )
162
162
  if file_size > MAX_FILE_SIZE:
163
- return http_error(400, "File is too big", f"Upload limit for file is {MAX_FILE_SIZE >> 20} MB")
163
+ return http_error(
164
+ 400,
165
+ "File is too big",
166
+ f"Upload limit for file is {MAX_FILE_SIZE >> 20} MB",
167
+ )
164
168
  with requests.get(url, stream=True) as r:
165
169
  if r.status_code != 200:
166
170
  return http_error(400, "Error getting file", f"Got status code: {r.status_code}")
@@ -209,11 +213,18 @@ class File(Resource):
209
213
 
210
214
  try:
211
215
  ca.file_controller.delete_file(name)
212
- except Exception as e:
213
- logger.error(e)
216
+ except FileNotFoundError:
217
+ logger.exception(f"Error when deleting file '{name}'")
214
218
  return http_error(
215
219
  400,
216
220
  "Error deleting file",
217
- f"There was an error while tring to delete file with name '{name}'",
221
+ f"There was an error while trying to delete file with name '{name}'",
222
+ )
223
+ except Exception as e:
224
+ logger.error(e)
225
+ return http_error(
226
+ 500,
227
+ "Error occured while deleting file",
228
+ f"There was an error while trying to delete file with name '{name}'",
218
229
  )
219
230
  return "", 200
@@ -19,6 +19,9 @@ from mindsdb.api.http.utils import http_error
19
19
  from mindsdb.api.http.namespaces.configs.handlers import ns_conf
20
20
  from mindsdb.api.executor.controllers.session_controller import SessionController
21
21
  from mindsdb.api.executor.command_executor import ExecuteCommands
22
+ from mindsdb.utilities import log
23
+
24
+ logger = log.getLogger(__name__)
22
25
 
23
26
 
24
27
  @ns_conf.route("/")
@@ -59,7 +62,9 @@ class HandlerIcon(Resource):
59
62
  if icon_path.is_absolute() is False:
60
63
  icon_path = Path(os.getcwd()).joinpath(icon_path)
61
64
  except Exception:
62
- return http_error(HTTPStatus.NOT_FOUND, "Icon not found", f"Icon for {handler_name} not found")
65
+ error_message = f"Icon for '{handler_name}' not found"
66
+ logger.warning(error_message)
67
+ return http_error(HTTPStatus.NOT_FOUND, "Icon not found", error_message)
63
68
  else:
64
69
  return send_file(icon_path)
65
70
 
@@ -170,7 +175,7 @@ class BYOMUpload(Resource):
170
175
  code_file_path = params["code"]
171
176
  try:
172
177
  module_file_path = params["modules"]
173
- except AttributeError:
178
+ except KeyError:
174
179
  module_file_path = Path(code_file_path).parent / "requirements.txt"
175
180
  module_file_path.touch()
176
181
  module_file_path = str(module_file_path)