MindsDB 25.7.2.0__py3-none-any.whl → 25.7.4.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 +1 -1
- mindsdb/api/a2a/common/server/server.py +16 -6
- mindsdb/api/executor/command_executor.py +213 -137
- mindsdb/api/executor/datahub/datanodes/integration_datanode.py +5 -1
- mindsdb/api/executor/datahub/datanodes/project_datanode.py +14 -3
- mindsdb/api/executor/planner/plan_join.py +3 -0
- mindsdb/api/executor/planner/plan_join_ts.py +117 -100
- mindsdb/api/executor/planner/query_planner.py +1 -0
- mindsdb/api/executor/sql_query/steps/apply_predictor_step.py +54 -85
- mindsdb/api/http/initialize.py +16 -43
- mindsdb/api/http/namespaces/agents.py +24 -21
- mindsdb/api/http/namespaces/chatbots.py +83 -120
- mindsdb/api/http/namespaces/file.py +1 -1
- mindsdb/api/http/namespaces/jobs.py +38 -60
- mindsdb/api/http/namespaces/tree.py +69 -61
- mindsdb/api/mcp/start.py +2 -0
- mindsdb/api/mysql/mysql_proxy/utilities/dump.py +3 -2
- mindsdb/integrations/handlers/autogluon_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/autosklearn_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/bigquery_handler/bigquery_handler.py +25 -5
- mindsdb/integrations/handlers/chromadb_handler/chromadb_handler.py +3 -3
- mindsdb/integrations/handlers/flaml_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/google_calendar_handler/google_calendar_tables.py +82 -73
- mindsdb/integrations/handlers/hubspot_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/langchain_handler/langchain_handler.py +83 -76
- mindsdb/integrations/handlers/lightwood_handler/requirements.txt +4 -4
- mindsdb/integrations/handlers/litellm_handler/litellm_handler.py +16 -3
- mindsdb/integrations/handlers/litellm_handler/settings.py +2 -1
- mindsdb/integrations/handlers/llama_index_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +106 -90
- mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +41 -39
- mindsdb/integrations/handlers/s3_handler/s3_handler.py +72 -70
- mindsdb/integrations/handlers/salesforce_handler/constants.py +208 -0
- mindsdb/integrations/handlers/salesforce_handler/salesforce_handler.py +142 -81
- mindsdb/integrations/handlers/salesforce_handler/salesforce_tables.py +12 -4
- mindsdb/integrations/handlers/slack_handler/slack_tables.py +141 -161
- mindsdb/integrations/handlers/tpot_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/web_handler/urlcrawl_helpers.py +32 -17
- mindsdb/integrations/handlers/web_handler/web_handler.py +19 -22
- mindsdb/integrations/handlers/youtube_handler/youtube_tables.py +183 -55
- mindsdb/integrations/libs/vectordatabase_handler.py +10 -1
- mindsdb/integrations/utilities/handler_utils.py +32 -12
- mindsdb/interfaces/agents/agents_controller.py +169 -110
- mindsdb/interfaces/agents/langchain_agent.py +10 -3
- mindsdb/interfaces/data_catalog/data_catalog_loader.py +22 -8
- mindsdb/interfaces/database/database.py +38 -13
- mindsdb/interfaces/database/integrations.py +20 -5
- mindsdb/interfaces/database/projects.py +63 -16
- mindsdb/interfaces/database/views.py +86 -60
- mindsdb/interfaces/jobs/jobs_controller.py +103 -110
- mindsdb/interfaces/knowledge_base/controller.py +33 -5
- mindsdb/interfaces/knowledge_base/evaluate.py +53 -9
- mindsdb/interfaces/knowledge_base/executor.py +24 -0
- mindsdb/interfaces/knowledge_base/llm_client.py +3 -3
- mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +21 -13
- mindsdb/interfaces/query_context/context_controller.py +100 -133
- mindsdb/interfaces/skills/skills_controller.py +18 -6
- mindsdb/interfaces/storage/db.py +40 -6
- mindsdb/interfaces/variables/variables_controller.py +8 -15
- mindsdb/utilities/config.py +3 -3
- mindsdb/utilities/functions.py +72 -60
- mindsdb/utilities/log.py +38 -6
- mindsdb/utilities/ps.py +7 -7
- {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/METADATA +262 -263
- {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/RECORD +69 -68
- {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/WHEEL +0 -0
- {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/top_level.txt +0 -0
mindsdb/__about__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
__title__ = "MindsDB"
|
|
2
2
|
__package_name__ = "mindsdb"
|
|
3
|
-
__version__ = "25.7.
|
|
3
|
+
__version__ = "25.7.4.0"
|
|
4
4
|
__description__ = "MindsDB's AI SQL Server enables developers to build AI tools that need access to real-time data to perform their tasks"
|
|
5
5
|
__email__ = "jorge@mindsdb.com"
|
|
6
6
|
__author__ = "MindsDB Inc"
|
mindsdb/__main__.py
CHANGED
|
@@ -375,7 +375,7 @@ if __name__ == "__main__":
|
|
|
375
375
|
apis = os.getenv("MINDSDB_APIS") or config.cmd_args.api
|
|
376
376
|
|
|
377
377
|
if apis is None: # If "--api" option is not specified, start the default APIs
|
|
378
|
-
api_arr = [TrunkProcessEnum.HTTP, TrunkProcessEnum.MYSQL]
|
|
378
|
+
api_arr = [TrunkProcessEnum.HTTP, TrunkProcessEnum.MYSQL, TrunkProcessEnum.MCP, TrunkProcessEnum.A2A]
|
|
379
379
|
elif apis == "": # If "--api=" (blank) is specified, don't start any APIs
|
|
380
380
|
api_arr = []
|
|
381
381
|
else: # The user has provided a list of APIs to start
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import time
|
|
3
|
+
from typing import AsyncIterable, Any, Dict
|
|
4
|
+
|
|
1
5
|
from starlette.applications import Starlette
|
|
2
6
|
from starlette.middleware.cors import CORSMiddleware
|
|
3
7
|
from starlette.responses import JSONResponse
|
|
@@ -19,14 +23,12 @@ from ...common.types import (
|
|
|
19
23
|
SendTaskStreamingRequest,
|
|
20
24
|
)
|
|
21
25
|
from pydantic import ValidationError
|
|
22
|
-
import json
|
|
23
|
-
import time
|
|
24
|
-
from typing import AsyncIterable, Any, Dict
|
|
25
26
|
from ...common.server.task_manager import TaskManager
|
|
26
27
|
|
|
27
|
-
import
|
|
28
|
+
from mindsdb.utilities import log
|
|
29
|
+
from mindsdb.utilities.log import get_uvicorn_logging_config, get_mindsdb_log_level
|
|
28
30
|
|
|
29
|
-
logger =
|
|
31
|
+
logger = log.getLogger(__name__)
|
|
30
32
|
|
|
31
33
|
|
|
32
34
|
class A2AServer:
|
|
@@ -68,7 +70,15 @@ class A2AServer:
|
|
|
68
70
|
import uvicorn
|
|
69
71
|
|
|
70
72
|
# Configure uvicorn with optimized settings for streaming
|
|
71
|
-
uvicorn.run(
|
|
73
|
+
uvicorn.run(
|
|
74
|
+
self.app,
|
|
75
|
+
host=self.host,
|
|
76
|
+
port=self.port,
|
|
77
|
+
http="h11",
|
|
78
|
+
timeout_keep_alive=65,
|
|
79
|
+
log_level=get_mindsdb_log_level(),
|
|
80
|
+
log_config=get_uvicorn_logging_config("uvicorn_a2a"),
|
|
81
|
+
)
|
|
72
82
|
|
|
73
83
|
def _get_agent_card(self, request: Request) -> JSONResponse:
|
|
74
84
|
return JSONResponse(self.agent_card.model_dump(exclude_none=True))
|
|
@@ -84,7 +84,7 @@ from mindsdb.api.mysql.mysql_proxy.libs.constants.mysql import (
|
|
|
84
84
|
TYPES,
|
|
85
85
|
)
|
|
86
86
|
|
|
87
|
-
from .exceptions import (
|
|
87
|
+
from mindsdb.api.executor.exceptions import (
|
|
88
88
|
ExecutorException,
|
|
89
89
|
BadDbError,
|
|
90
90
|
NotSupportedYet,
|
|
@@ -154,6 +154,64 @@ def _get_show_where(
|
|
|
154
154
|
return None
|
|
155
155
|
|
|
156
156
|
|
|
157
|
+
def match_one_part_name(identifier: Identifier, ensure_lower_case: bool = False) -> str:
|
|
158
|
+
"""Extract a single-part name from an Identifier object, optionally ensuring it is lowercase.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
identifier (Identifier): The identifier to extract the name from. Must contain exactly one part.
|
|
162
|
+
ensure_lower_case (bool, optional): If True, raises ValueError if the name is not lowercase. Defaults to False.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
str: The extracted name, converted to lowercase if not quoted.
|
|
166
|
+
|
|
167
|
+
Raises:
|
|
168
|
+
ValueError: If the identifier does not contain exactly one part, or if ensure_lower_case is True and the name is not lowercase.
|
|
169
|
+
"""
|
|
170
|
+
match identifier.parts, identifier.is_quoted:
|
|
171
|
+
case [name], [is_quoted]:
|
|
172
|
+
...
|
|
173
|
+
case _:
|
|
174
|
+
raise ValueError(f"Only single-part names are allowed: {identifier}")
|
|
175
|
+
if not is_quoted:
|
|
176
|
+
name = name.lower()
|
|
177
|
+
if ensure_lower_case and not name.islower():
|
|
178
|
+
raise ValueError(f"The name must be in lowercase: {identifier}")
|
|
179
|
+
return name
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def match_two_part_name(
|
|
183
|
+
identifier: Identifier, ensure_lower_case: bool = False, default_db_name: str | None = None
|
|
184
|
+
) -> tuple[str, str]:
|
|
185
|
+
"""Extract a (database, name) tuple from an Identifier object that may have one or two parts.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
identifier (Identifier): The identifier to extract names from. Must contain one or two parts.
|
|
189
|
+
ensure_lower_case (bool, optional): If True, raises ValueError if the name part is not lowercase. Defaults to False.
|
|
190
|
+
default_db_name (str | None, optional): The default database name to use if only one part is provided. Defaults to None.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
tuple[str, str]: A tuple of (database_name, name), where database_name may be None if not provided and no default is given.
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
ValueError: If the identifier does not contain one or two parts, or if ensure_lower_case is True and the name is not lowercase.
|
|
197
|
+
"""
|
|
198
|
+
db_name = None
|
|
199
|
+
match identifier.parts, identifier.is_quoted:
|
|
200
|
+
case [name], [is_quoted]:
|
|
201
|
+
...
|
|
202
|
+
case [db_name, name], [_, is_quoted]:
|
|
203
|
+
...
|
|
204
|
+
case _:
|
|
205
|
+
raise ValueError(f"Only single-part or two-part names are allowed: {identifier}")
|
|
206
|
+
if not is_quoted:
|
|
207
|
+
name = name.lower()
|
|
208
|
+
if ensure_lower_case and not name.islower():
|
|
209
|
+
raise ValueError(f"The name must be in lowercase: {identifier}")
|
|
210
|
+
if db_name is None:
|
|
211
|
+
db_name = default_db_name
|
|
212
|
+
return db_name, name
|
|
213
|
+
|
|
214
|
+
|
|
157
215
|
class ExecuteCommands:
|
|
158
216
|
def __init__(self, session, context=None):
|
|
159
217
|
if context is None:
|
|
@@ -177,19 +235,11 @@ class ExecuteCommands:
|
|
|
177
235
|
if statement_type is CreateDatabase:
|
|
178
236
|
return self.answer_create_database(statement)
|
|
179
237
|
elif statement_type is CreateMLEngine:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return self.answer_create_ml_engine(
|
|
183
|
-
name,
|
|
184
|
-
handler=statement.handler,
|
|
185
|
-
params=statement.params,
|
|
186
|
-
if_not_exists=getattr(statement, "if_not_exists", False),
|
|
187
|
-
)
|
|
238
|
+
return self.answer_create_ml_engine(statement)
|
|
188
239
|
elif statement_type is DropMLEngine:
|
|
189
240
|
return self.answer_drop_ml_engine(statement)
|
|
190
241
|
elif statement_type is DropPredictor:
|
|
191
242
|
return self.answer_drop_model(statement, database_name)
|
|
192
|
-
|
|
193
243
|
elif statement_type is DropTables:
|
|
194
244
|
return self.answer_drop_tables(statement, database_name)
|
|
195
245
|
elif statement_type is DropDatasource or statement_type is DropDatabase:
|
|
@@ -660,10 +710,9 @@ class ExecuteCommands:
|
|
|
660
710
|
|
|
661
711
|
def answer_create_trigger(self, statement, database_name):
|
|
662
712
|
triggers_controller = TriggersController()
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
project_name = name.parts[-2] if len(name.parts) > 1 else database_name
|
|
713
|
+
project_name, trigger_name = match_two_part_name(
|
|
714
|
+
statement.name, ensure_lower_case=True, default_db_name=database_name
|
|
715
|
+
)
|
|
667
716
|
|
|
668
717
|
triggers_controller.add(
|
|
669
718
|
trigger_name,
|
|
@@ -687,10 +736,9 @@ class ExecuteCommands:
|
|
|
687
736
|
|
|
688
737
|
def answer_create_job(self, statement: CreateJob, database_name):
|
|
689
738
|
jobs_controller = JobsController()
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
project_name = name.parts[-2] if len(name.parts) > 1 else database_name
|
|
739
|
+
project_name, job_name = match_two_part_name(
|
|
740
|
+
statement.name, ensure_lower_case=True, default_db_name=database_name
|
|
741
|
+
)
|
|
694
742
|
|
|
695
743
|
try:
|
|
696
744
|
jobs_controller.create(job_name, project_name, statement)
|
|
@@ -702,10 +750,8 @@ class ExecuteCommands:
|
|
|
702
750
|
|
|
703
751
|
def answer_drop_job(self, statement, database_name):
|
|
704
752
|
jobs_controller = JobsController()
|
|
753
|
+
project_name, job_name = match_two_part_name(statement.name, default_db_name=database_name)
|
|
705
754
|
|
|
706
|
-
name = statement.name
|
|
707
|
-
job_name = name.parts[-1]
|
|
708
|
-
project_name = name.parts[-2] if len(name.parts) > 1 else database_name
|
|
709
755
|
try:
|
|
710
756
|
jobs_controller.delete(job_name, project_name)
|
|
711
757
|
except EntityNotExistsError:
|
|
@@ -718,9 +764,8 @@ class ExecuteCommands:
|
|
|
718
764
|
|
|
719
765
|
def answer_create_chatbot(self, statement, database_name):
|
|
720
766
|
chatbot_controller = ChatBotController()
|
|
767
|
+
project_name, name = match_two_part_name(statement.name, ensure_lower_case=True, default_db_name=database_name)
|
|
721
768
|
|
|
722
|
-
name = statement.name
|
|
723
|
-
project_name = name.parts[-2] if len(name.parts) > 1 else database_name
|
|
724
769
|
is_running = statement.params.pop("is_running", True)
|
|
725
770
|
|
|
726
771
|
database = self.session.integration_controller.get(statement.database.parts[-1])
|
|
@@ -738,7 +783,7 @@ class ExecuteCommands:
|
|
|
738
783
|
if statement.agent is not None:
|
|
739
784
|
agent_name = statement.agent.parts[-1]
|
|
740
785
|
chatbot_controller.add_chatbot(
|
|
741
|
-
name
|
|
786
|
+
name,
|
|
742
787
|
project_name=project_name,
|
|
743
788
|
model_name=model_name,
|
|
744
789
|
agent_name=agent_name,
|
|
@@ -912,14 +957,12 @@ class ExecuteCommands:
|
|
|
912
957
|
return ExecuteAnswer(data=ResultSet.from_df(df, table_name=""))
|
|
913
958
|
|
|
914
959
|
def answer_create_kb_index(self, statement, database_name):
|
|
915
|
-
table_name = statement.name
|
|
916
|
-
project_name = statement.name.parts[0] if len(statement.name.parts) > 1 else database_name
|
|
960
|
+
project_name, table_name = match_two_part_name(statement.name, default_db_name=database_name)
|
|
917
961
|
self.session.kb_controller.create_index(table_name=table_name, project_name=project_name)
|
|
918
962
|
return ExecuteAnswer()
|
|
919
963
|
|
|
920
964
|
def answer_evaluate_kb(self, statement: EvaluateKnowledgeBase, database_name):
|
|
921
|
-
table_name = statement.name
|
|
922
|
-
project_name = statement.name.parts[0] if len(statement.name.parts) > 1 else database_name
|
|
965
|
+
project_name, table_name = match_two_part_name(statement.name, default_db_name=database_name)
|
|
923
966
|
scores = self.session.kb_controller.evaluate(
|
|
924
967
|
table_name=table_name, project_name=project_name, params=statement.params
|
|
925
968
|
)
|
|
@@ -928,6 +971,7 @@ class ExecuteCommands:
|
|
|
928
971
|
def _get_model_info(self, identifier, except_absent=True, database_name=None):
|
|
929
972
|
if len(identifier.parts) == 1:
|
|
930
973
|
identifier.parts = [database_name, identifier.parts[0]]
|
|
974
|
+
identifier.is_quoted = [False] + identifier.is_quoted
|
|
931
975
|
|
|
932
976
|
database_name, model_name, model_version = resolve_model_identifier(identifier)
|
|
933
977
|
if database_name is None:
|
|
@@ -1105,7 +1149,24 @@ class ExecuteCommands:
|
|
|
1105
1149
|
handler = self.session.integration_controller.get_data_handler(name, connect=False)
|
|
1106
1150
|
handler.handler_storage.import_files(storage)
|
|
1107
1151
|
|
|
1108
|
-
def answer_create_ml_engine(self,
|
|
1152
|
+
def answer_create_ml_engine(self, statement: CreateMLEngine) -> ExecuteAnswer:
|
|
1153
|
+
"""Handles the `CREATE ML_ENGINE` command, which creates a new ML integration (engine) in the system.
|
|
1154
|
+
|
|
1155
|
+
Args:
|
|
1156
|
+
statement (CreateMLEngine): The AST object representing the CREATE ML_ENGINE command.
|
|
1157
|
+
|
|
1158
|
+
Returns:
|
|
1159
|
+
ExecuteAnswer: The result of the ML engine creation operation.
|
|
1160
|
+
|
|
1161
|
+
Raises:
|
|
1162
|
+
ValueError: If the ml_engine name format is invalid.
|
|
1163
|
+
"""
|
|
1164
|
+
name = match_one_part_name(statement.name, ensure_lower_case=True)
|
|
1165
|
+
|
|
1166
|
+
handler = statement.handler
|
|
1167
|
+
params = statement.params
|
|
1168
|
+
if_not_exists = getattr(statement, "if_not_exists", False)
|
|
1169
|
+
|
|
1109
1170
|
integrations = self.session.integration_controller.get_all()
|
|
1110
1171
|
if name in integrations:
|
|
1111
1172
|
if not if_not_exists:
|
|
@@ -1147,8 +1208,21 @@ class ExecuteCommands:
|
|
|
1147
1208
|
|
|
1148
1209
|
return ExecuteAnswer()
|
|
1149
1210
|
|
|
1150
|
-
def answer_drop_ml_engine(self, statement:
|
|
1151
|
-
|
|
1211
|
+
def answer_drop_ml_engine(self, statement: DropMLEngine) -> ExecuteAnswer:
|
|
1212
|
+
"""Handles the `DROP ML_ENGINE` command, which removes an ML integration (engine) from the system.
|
|
1213
|
+
|
|
1214
|
+
Args:
|
|
1215
|
+
statement (DropMLEngine): The AST object representing the DROP ML_ENGINE command.
|
|
1216
|
+
|
|
1217
|
+
Raises:
|
|
1218
|
+
EntityNotExistsError: If the integration does not exist and IF EXISTS is not specified.
|
|
1219
|
+
ValueError: If the integration name is provided in an invalid format.
|
|
1220
|
+
|
|
1221
|
+
Returns:
|
|
1222
|
+
ExecuteAnswer: The result of the ML engine deletion operation.
|
|
1223
|
+
"""
|
|
1224
|
+
name = match_one_part_name(statement.name)
|
|
1225
|
+
|
|
1152
1226
|
integrations = self.session.integration_controller.get_all()
|
|
1153
1227
|
if name not in integrations:
|
|
1154
1228
|
if not statement.if_exists:
|
|
@@ -1158,53 +1232,57 @@ class ExecuteCommands:
|
|
|
1158
1232
|
self.session.integration_controller.delete(name)
|
|
1159
1233
|
return ExecuteAnswer()
|
|
1160
1234
|
|
|
1161
|
-
def answer_create_database(self, statement:
|
|
1162
|
-
"""
|
|
1235
|
+
def answer_create_database(self, statement: CreateDatabase) -> ExecuteAnswer:
|
|
1236
|
+
"""Create new integration or project
|
|
1237
|
+
|
|
1163
1238
|
Args:
|
|
1164
|
-
statement (
|
|
1239
|
+
statement (CreateDatabase): data for creating database/project
|
|
1240
|
+
|
|
1241
|
+
Returns:
|
|
1242
|
+
ExecuteAnswer: 'ok' answer
|
|
1165
1243
|
"""
|
|
1244
|
+
database_name = match_one_part_name(statement.name, ensure_lower_case=True)
|
|
1166
1245
|
|
|
1167
|
-
|
|
1168
|
-
raise Exception("Database name should contain only 1 part.")
|
|
1246
|
+
engine = (statement.engine or "mindsdb").lower()
|
|
1169
1247
|
|
|
1170
|
-
database_name = statement.name.parts[0]
|
|
1171
|
-
engine = statement.engine
|
|
1172
|
-
if engine is None:
|
|
1173
|
-
engine = "mindsdb"
|
|
1174
|
-
engine = engine.lower()
|
|
1175
1248
|
connection_args = statement.parameters
|
|
1176
1249
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1250
|
+
try:
|
|
1251
|
+
if engine == "mindsdb":
|
|
1179
1252
|
ProjectController().add(database_name)
|
|
1180
|
-
|
|
1181
|
-
if statement.if_not_exists is False:
|
|
1182
|
-
raise
|
|
1183
|
-
else:
|
|
1184
|
-
try:
|
|
1253
|
+
else:
|
|
1185
1254
|
self._create_integration(database_name, engine, connection_args)
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1255
|
+
except EntityExistsError:
|
|
1256
|
+
if statement.if_not_exists is False:
|
|
1257
|
+
raise
|
|
1189
1258
|
|
|
1190
1259
|
return ExecuteAnswer()
|
|
1191
1260
|
|
|
1192
|
-
def answer_drop_database(self, statement):
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1261
|
+
def answer_drop_database(self, statement: DropDatabase | DropDatasource) -> ExecuteAnswer:
|
|
1262
|
+
"""Drop a database (project or integration) by name.
|
|
1263
|
+
|
|
1264
|
+
Args:
|
|
1265
|
+
statement (DropDatabase | DropDatasource): The parsed DROP DATABASE or DROP DATASOURCE statement.
|
|
1266
|
+
|
|
1267
|
+
Raises:
|
|
1268
|
+
Exception: If the database name format is invalid.
|
|
1269
|
+
EntityNotExistsError: If the database does not exist and 'IF EXISTS' is not specified in the statement.
|
|
1270
|
+
|
|
1271
|
+
Returns:
|
|
1272
|
+
ExecuteAnswer: The result of the drop database operation.
|
|
1273
|
+
"""
|
|
1274
|
+
db_name = match_one_part_name(statement.name)
|
|
1275
|
+
|
|
1196
1276
|
try:
|
|
1197
|
-
self.session.database_controller.delete(db_name)
|
|
1277
|
+
self.session.database_controller.delete(db_name, strict_case=statement.name.is_quoted[0])
|
|
1198
1278
|
except EntityNotExistsError:
|
|
1199
1279
|
if statement.if_exists is not True:
|
|
1200
1280
|
raise
|
|
1201
1281
|
return ExecuteAnswer()
|
|
1202
1282
|
|
|
1203
|
-
def answer_alter_database(self, statement):
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
db_name = statement.name.parts[0]
|
|
1207
|
-
self.session.database_controller.update(db_name, data=statement.params)
|
|
1283
|
+
def answer_alter_database(self, statement: AlterDatabase) -> ExecuteAnswer:
|
|
1284
|
+
db_name = match_one_part_name(statement.name)
|
|
1285
|
+
self.session.database_controller.update(db_name, data=statement.params, strict_case=statement.name.is_quoted[0])
|
|
1208
1286
|
return ExecuteAnswer()
|
|
1209
1287
|
|
|
1210
1288
|
def answer_drop_tables(self, statement, database_name):
|
|
@@ -1221,9 +1299,11 @@ class ExecuteCommands:
|
|
|
1221
1299
|
db_name = database_name
|
|
1222
1300
|
|
|
1223
1301
|
dn = self.session.datahub[db_name]
|
|
1302
|
+
if dn is None:
|
|
1303
|
+
raise ExecutorException(f"Cannot delete a table from database '{db_name}': the database does not exist")
|
|
1304
|
+
|
|
1224
1305
|
if db_name is not None:
|
|
1225
1306
|
dn.drop_table(table, if_exists=statement.if_exists)
|
|
1226
|
-
|
|
1227
1307
|
elif db_name in self.session.database_controller.get_dict(filter_type="project"):
|
|
1228
1308
|
# TODO do we need feature: delete object from project via drop table?
|
|
1229
1309
|
|
|
@@ -1240,35 +1320,19 @@ class ExecuteCommands:
|
|
|
1240
1320
|
|
|
1241
1321
|
return ExecuteAnswer()
|
|
1242
1322
|
|
|
1243
|
-
def answer_create_or_alter_view(self, statement:
|
|
1323
|
+
def answer_create_or_alter_view(self, statement: CreateView | AlterView, database_name: str) -> ExecuteAnswer:
|
|
1244
1324
|
"""Process CREATE and ALTER VIEW commands
|
|
1245
1325
|
|
|
1246
1326
|
Args:
|
|
1247
|
-
statement (
|
|
1327
|
+
statement (CreateView | AlterView): data for creating or altering view
|
|
1248
1328
|
database_name (str): name of the current database
|
|
1249
1329
|
|
|
1250
1330
|
Returns:
|
|
1251
1331
|
ExecuteAnswer: answer for the command
|
|
1252
1332
|
"""
|
|
1253
|
-
project_name =
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
parts = statement.name.split(".")
|
|
1257
|
-
elif isinstance(statement.name, Identifier):
|
|
1258
|
-
parts = statement.name.parts
|
|
1259
|
-
else:
|
|
1260
|
-
raise ValueError(f"Unknown type of view name: {statement.name}")
|
|
1261
|
-
|
|
1262
|
-
match parts:
|
|
1263
|
-
case [project_name, view_name]:
|
|
1264
|
-
pass
|
|
1265
|
-
case [view_name]:
|
|
1266
|
-
pass
|
|
1267
|
-
case _:
|
|
1268
|
-
raise ValueError(
|
|
1269
|
-
'View name should be in the form "project_name.view_name" '
|
|
1270
|
-
f'or "view_name", got {statement.name.parts}'
|
|
1271
|
-
)
|
|
1333
|
+
project_name, view_name = match_two_part_name(
|
|
1334
|
+
statement.name, default_db_name=database_name, ensure_lower_case=isinstance(statement, CreateView)
|
|
1335
|
+
)
|
|
1272
1336
|
|
|
1273
1337
|
query_str = statement.query_str
|
|
1274
1338
|
|
|
@@ -1301,7 +1365,7 @@ class ExecuteCommands:
|
|
|
1301
1365
|
raise
|
|
1302
1366
|
elif isinstance(statement, AlterView):
|
|
1303
1367
|
try:
|
|
1304
|
-
project.update_view(view_name, query=query_str)
|
|
1368
|
+
project.update_view(view_name, query=query_str, strict_case=(not view_name.islower()))
|
|
1305
1369
|
except EntityNotExistsError:
|
|
1306
1370
|
raise ExecutorException(f"View {view_name} does not exist in {project_name}")
|
|
1307
1371
|
else:
|
|
@@ -1309,19 +1373,33 @@ class ExecuteCommands:
|
|
|
1309
1373
|
|
|
1310
1374
|
return ExecuteAnswer()
|
|
1311
1375
|
|
|
1312
|
-
def answer_drop_view(self, statement, database_name):
|
|
1313
|
-
|
|
1376
|
+
def answer_drop_view(self, statement: DropView, database_name: str) -> ExecuteAnswer:
|
|
1377
|
+
"""Drop one or more views from the specified database/project.
|
|
1314
1378
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1379
|
+
Args:
|
|
1380
|
+
statement (DropView): The parsed DROP VIEW statement containing view names and options.
|
|
1381
|
+
database_name (str): The name of the database (project) from which to drop the views.
|
|
1382
|
+
|
|
1383
|
+
Raises:
|
|
1384
|
+
EntityNotExistsError: If a view does not exist and 'IF EXISTS' is not specified in the statement.
|
|
1385
|
+
ValueError: If the view name format is invalid.
|
|
1386
|
+
|
|
1387
|
+
Returns:
|
|
1388
|
+
ExecuteAnswer: The result of the drop view operation.
|
|
1389
|
+
"""
|
|
1390
|
+
for name in statement.names:
|
|
1391
|
+
match name.parts, name.is_quoted:
|
|
1392
|
+
case [view_name], [view_name_quoted]:
|
|
1393
|
+
db_name_quoted = False
|
|
1394
|
+
case [database_name, view_name], [db_name_quoted, view_name_quoted]:
|
|
1395
|
+
pass
|
|
1396
|
+
case _:
|
|
1397
|
+
raise ValueError(f"Invalid view name: {name}")
|
|
1398
|
+
|
|
1399
|
+
project = self.session.database_controller.get_project(database_name, db_name_quoted)
|
|
1322
1400
|
|
|
1323
1401
|
try:
|
|
1324
|
-
project.drop_view(view_name)
|
|
1402
|
+
project.drop_view(view_name, strict_case=view_name_quoted)
|
|
1325
1403
|
except EntityNotExistsError:
|
|
1326
1404
|
if statement.if_exists is not True:
|
|
1327
1405
|
raise
|
|
@@ -1335,7 +1413,9 @@ class ExecuteCommands:
|
|
|
1335
1413
|
"Please pass the model parameters as a JSON object in the embedding_model field."
|
|
1336
1414
|
)
|
|
1337
1415
|
|
|
1338
|
-
project_name =
|
|
1416
|
+
project_name, kb_name = match_two_part_name(
|
|
1417
|
+
statement.name, ensure_lower_case=True, default_db_name=database_name
|
|
1418
|
+
)
|
|
1339
1419
|
|
|
1340
1420
|
if statement.storage is not None:
|
|
1341
1421
|
if len(statement.storage.parts) != 2:
|
|
@@ -1347,8 +1427,6 @@ class ExecuteCommands:
|
|
|
1347
1427
|
# TODO: implement this
|
|
1348
1428
|
raise ExecutorException("Create a knowledge base from a select is not supported yet")
|
|
1349
1429
|
|
|
1350
|
-
kb_name = statement.name.parts[-1]
|
|
1351
|
-
|
|
1352
1430
|
# create the knowledge base
|
|
1353
1431
|
_ = self.session.kb_controller.add(
|
|
1354
1432
|
name=kb_name,
|
|
@@ -1361,13 +1439,12 @@ class ExecuteCommands:
|
|
|
1361
1439
|
|
|
1362
1440
|
return ExecuteAnswer()
|
|
1363
1441
|
|
|
1364
|
-
def answer_drop_kb(self, statement: DropKnowledgeBase, database_name: str):
|
|
1365
|
-
|
|
1366
|
-
project_name = statement.name.parts[0] if len(statement.name.parts) > 1 else database_name
|
|
1442
|
+
def answer_drop_kb(self, statement: DropKnowledgeBase, database_name: str) -> ExecuteAnswer:
|
|
1443
|
+
project_name, kb_name = match_two_part_name(statement.name, default_db_name=database_name)
|
|
1367
1444
|
|
|
1368
1445
|
# delete the knowledge base
|
|
1369
1446
|
self.session.kb_controller.delete(
|
|
1370
|
-
name=
|
|
1447
|
+
name=kb_name,
|
|
1371
1448
|
project_name=project_name,
|
|
1372
1449
|
if_exists=statement.if_exists,
|
|
1373
1450
|
)
|
|
@@ -1375,8 +1452,7 @@ class ExecuteCommands:
|
|
|
1375
1452
|
return ExecuteAnswer()
|
|
1376
1453
|
|
|
1377
1454
|
def answer_create_skill(self, statement, database_name):
|
|
1378
|
-
name = statement.name
|
|
1379
|
-
project_name = statement.name.parts[0] if len(statement.name.parts) > 1 else database_name
|
|
1455
|
+
project_name, name = match_two_part_name(statement.name, ensure_lower_case=True, default_db_name=database_name)
|
|
1380
1456
|
|
|
1381
1457
|
try:
|
|
1382
1458
|
_ = self.session.skills_controller.add_skill(name, project_name, statement.type, statement.params)
|
|
@@ -1387,11 +1463,10 @@ class ExecuteCommands:
|
|
|
1387
1463
|
return ExecuteAnswer()
|
|
1388
1464
|
|
|
1389
1465
|
def answer_drop_skill(self, statement, database_name):
|
|
1390
|
-
name = statement.name
|
|
1391
|
-
project_name = statement.name.parts[0] if len(statement.name.parts) > 1 else database_name
|
|
1466
|
+
project_name, name = match_two_part_name(statement.name, default_db_name=database_name)
|
|
1392
1467
|
|
|
1393
1468
|
try:
|
|
1394
|
-
self.session.skills_controller.delete_skill(name, project_name)
|
|
1469
|
+
self.session.skills_controller.delete_skill(name, project_name, strict_case=True)
|
|
1395
1470
|
except ValueError as e:
|
|
1396
1471
|
# Project does not exist or skill does not exist.
|
|
1397
1472
|
raise ExecutorException(str(e))
|
|
@@ -1399,8 +1474,7 @@ class ExecuteCommands:
|
|
|
1399
1474
|
return ExecuteAnswer()
|
|
1400
1475
|
|
|
1401
1476
|
def answer_update_skill(self, statement, database_name):
|
|
1402
|
-
name = statement.name
|
|
1403
|
-
project_name = statement.name.parts[0] if len(statement.name.parts) > 1 else database_name
|
|
1477
|
+
project_name, name = match_two_part_name(statement.name, default_db_name=database_name)
|
|
1404
1478
|
|
|
1405
1479
|
type = statement.params.pop("type", None)
|
|
1406
1480
|
try:
|
|
@@ -1414,8 +1488,7 @@ class ExecuteCommands:
|
|
|
1414
1488
|
return ExecuteAnswer()
|
|
1415
1489
|
|
|
1416
1490
|
def answer_create_agent(self, statement, database_name):
|
|
1417
|
-
name = statement.name
|
|
1418
|
-
project_name = statement.name.parts[0] if len(statement.name.parts) > 1 else database_name
|
|
1491
|
+
project_name, name = match_two_part_name(statement.name, ensure_lower_case=True, default_db_name=database_name)
|
|
1419
1492
|
|
|
1420
1493
|
skills = statement.params.pop("skills", [])
|
|
1421
1494
|
provider = statement.params.pop("provider", None)
|
|
@@ -1428,15 +1501,17 @@ class ExecuteCommands:
|
|
|
1428
1501
|
provider=provider,
|
|
1429
1502
|
params=statement.params,
|
|
1430
1503
|
)
|
|
1504
|
+
except EntityExistsError as e:
|
|
1505
|
+
if statement.if_not_exists is not True:
|
|
1506
|
+
raise ExecutorException(str(e))
|
|
1431
1507
|
except ValueError as e:
|
|
1432
1508
|
# Project does not exist or agent already exists.
|
|
1433
1509
|
raise ExecutorException(str(e))
|
|
1434
1510
|
|
|
1435
1511
|
return ExecuteAnswer()
|
|
1436
1512
|
|
|
1437
|
-
def answer_drop_agent(self, statement, database_name):
|
|
1438
|
-
name = statement.name
|
|
1439
|
-
project_name = statement.name.parts[0] if len(statement.name.parts) > 1 else database_name
|
|
1513
|
+
def answer_drop_agent(self, statement: DropAgent, database_name: str):
|
|
1514
|
+
project_name, name = match_two_part_name(statement.name, default_db_name=database_name)
|
|
1440
1515
|
|
|
1441
1516
|
try:
|
|
1442
1517
|
self.session.agents_controller.delete_agent(name, project_name)
|
|
@@ -1446,9 +1521,8 @@ class ExecuteCommands:
|
|
|
1446
1521
|
|
|
1447
1522
|
return ExecuteAnswer()
|
|
1448
1523
|
|
|
1449
|
-
def answer_update_agent(self, statement, database_name):
|
|
1450
|
-
name = statement.name
|
|
1451
|
-
project_name = statement.name.parts[0] if len(statement.name.parts) > 1 else database_name
|
|
1524
|
+
def answer_update_agent(self, statement: UpdateAgent, database_name: str):
|
|
1525
|
+
project_name, name = match_two_part_name(statement.name, default_db_name=database_name)
|
|
1452
1526
|
|
|
1453
1527
|
model = statement.params.pop("model", None)
|
|
1454
1528
|
skills_to_add = statement.params.pop("skills_to_add", [])
|
|
@@ -1469,14 +1543,13 @@ class ExecuteCommands:
|
|
|
1469
1543
|
return ExecuteAnswer()
|
|
1470
1544
|
|
|
1471
1545
|
@mark_process("learn")
|
|
1472
|
-
def answer_create_predictor(self, statement: CreatePredictor, database_name):
|
|
1473
|
-
integration_name =
|
|
1546
|
+
def answer_create_predictor(self, statement: CreatePredictor, database_name: str):
|
|
1547
|
+
integration_name, model_name = match_two_part_name(
|
|
1548
|
+
statement.name, ensure_lower_case=True, default_db_name=database_name
|
|
1549
|
+
)
|
|
1474
1550
|
|
|
1475
|
-
# allow creation in non-active projects, e.g. 'create mode proj.model' works whether `proj` is active or not
|
|
1476
|
-
if len(statement.name.parts) > 1:
|
|
1477
|
-
integration_name = statement.name.parts[0]
|
|
1478
|
-
model_name = statement.name.parts[-1]
|
|
1479
1551
|
statement.name.parts = [integration_name.lower(), model_name]
|
|
1552
|
+
statement.name.is_quoted = [False, False]
|
|
1480
1553
|
|
|
1481
1554
|
ml_integration_name = "lightwood" # default
|
|
1482
1555
|
if statement.using is not None:
|
|
@@ -1493,7 +1566,9 @@ class ExecuteCommands:
|
|
|
1493
1566
|
ml_handler = self.session.integration_controller.get_ml_handler(ml_integration_name)
|
|
1494
1567
|
except EntityNotExistsError:
|
|
1495
1568
|
# not exist, try to create it with same name as handler
|
|
1496
|
-
self.answer_create_ml_engine(
|
|
1569
|
+
self.answer_create_ml_engine(
|
|
1570
|
+
CreateMLEngine(name=Identifier(ml_integration_name), handler=ml_integration_name)
|
|
1571
|
+
)
|
|
1497
1572
|
|
|
1498
1573
|
ml_handler = self.session.integration_controller.get_ml_handler(ml_integration_name)
|
|
1499
1574
|
|
|
@@ -1506,7 +1581,6 @@ class ExecuteCommands:
|
|
|
1506
1581
|
|
|
1507
1582
|
try:
|
|
1508
1583
|
df = self.session.model_controller.create_model(statement, ml_handler)
|
|
1509
|
-
|
|
1510
1584
|
return ExecuteAnswer(data=ResultSet.from_df(df))
|
|
1511
1585
|
except EntityExistsError:
|
|
1512
1586
|
if getattr(statement, "if_not_exists", False) is True:
|
|
@@ -1921,22 +1995,24 @@ class ExecuteCommands:
|
|
|
1921
1995
|
self.session.model_controller.set_model_active_version(project_name, model_name, version)
|
|
1922
1996
|
return ExecuteAnswer()
|
|
1923
1997
|
|
|
1924
|
-
def answer_drop_model(self, statement, database_name):
|
|
1925
|
-
|
|
1926
|
-
version
|
|
1998
|
+
def answer_drop_model(self, statement: DropPredictor, database_name: str) -> ExecuteAnswer:
|
|
1999
|
+
"""Handles the DROP MODEL (or DROP PREDICTOR) command, which removes a model
|
|
2000
|
+
or a specific model version from a project.
|
|
1927
2001
|
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
model_parts = model_parts[:-1]
|
|
2002
|
+
Args:
|
|
2003
|
+
statement (DropPredictor): The AST object representing the DROP MODEL or DROP PREDICTOR command.
|
|
2004
|
+
database_name (str): The name of the current database/project.
|
|
1932
2005
|
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
2006
|
+
Raises:
|
|
2007
|
+
EntityNotExistsError: If the model or version does not exist and IF EXISTS is not specified.
|
|
2008
|
+
ValueError: If the model name format is invalid.
|
|
2009
|
+
|
|
2010
|
+
Returns:
|
|
2011
|
+
ExecuteAnswer: The result of the model deletion operation.
|
|
2012
|
+
"""
|
|
2013
|
+
project_name, model_name, version = resolve_model_identifier(statement.name)
|
|
2014
|
+
if project_name is None:
|
|
1937
2015
|
project_name = database_name
|
|
1938
|
-
else:
|
|
1939
|
-
raise ExecutorException(f"Unknown model: {statement.name}")
|
|
1940
2016
|
|
|
1941
2017
|
if version is not None:
|
|
1942
2018
|
# delete version
|