MindsDB 25.7.3.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/api/a2a/common/server/server.py +16 -6
- mindsdb/api/executor/command_executor.py +206 -135
- 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 +23 -20
- 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 +5 -2
- mindsdb/integrations/handlers/litellm_handler/settings.py +2 -1
- mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +106 -90
- mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +41 -39
- mindsdb/integrations/handlers/salesforce_handler/constants.py +208 -0
- mindsdb/integrations/handlers/salesforce_handler/salesforce_handler.py +141 -80
- mindsdb/integrations/handlers/salesforce_handler/salesforce_tables.py +0 -1
- 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/libs/vectordatabase_handler.py +10 -1
- mindsdb/integrations/utilities/handler_utils.py +32 -12
- mindsdb/interfaces/agents/agents_controller.py +167 -108
- mindsdb/interfaces/agents/langchain_agent.py +10 -3
- mindsdb/interfaces/data_catalog/data_catalog_loader.py +4 -4
- 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 +26 -5
- mindsdb/interfaces/knowledge_base/evaluate.py +2 -1
- mindsdb/interfaces/knowledge_base/executor.py +24 -0
- 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.3.0.dist-info → mindsdb-25.7.4.0.dist-info}/METADATA +246 -247
- {mindsdb-25.7.3.0.dist-info → mindsdb-25.7.4.0.dist-info}/RECORD +61 -60
- {mindsdb-25.7.3.0.dist-info → mindsdb-25.7.4.0.dist-info}/WHEEL +0 -0
- {mindsdb-25.7.3.0.dist-info → mindsdb-25.7.4.0.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.7.3.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"
|
|
@@ -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))
|
|
@@ -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):
|
|
@@ -1242,35 +1320,19 @@ class ExecuteCommands:
|
|
|
1242
1320
|
|
|
1243
1321
|
return ExecuteAnswer()
|
|
1244
1322
|
|
|
1245
|
-
def answer_create_or_alter_view(self, statement:
|
|
1323
|
+
def answer_create_or_alter_view(self, statement: CreateView | AlterView, database_name: str) -> ExecuteAnswer:
|
|
1246
1324
|
"""Process CREATE and ALTER VIEW commands
|
|
1247
1325
|
|
|
1248
1326
|
Args:
|
|
1249
|
-
statement (
|
|
1327
|
+
statement (CreateView | AlterView): data for creating or altering view
|
|
1250
1328
|
database_name (str): name of the current database
|
|
1251
1329
|
|
|
1252
1330
|
Returns:
|
|
1253
1331
|
ExecuteAnswer: answer for the command
|
|
1254
1332
|
"""
|
|
1255
|
-
project_name =
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
parts = statement.name.split(".")
|
|
1259
|
-
elif isinstance(statement.name, Identifier):
|
|
1260
|
-
parts = statement.name.parts
|
|
1261
|
-
else:
|
|
1262
|
-
raise ValueError(f"Unknown type of view name: {statement.name}")
|
|
1263
|
-
|
|
1264
|
-
match parts:
|
|
1265
|
-
case [project_name, view_name]:
|
|
1266
|
-
pass
|
|
1267
|
-
case [view_name]:
|
|
1268
|
-
pass
|
|
1269
|
-
case _:
|
|
1270
|
-
raise ValueError(
|
|
1271
|
-
'View name should be in the form "project_name.view_name" '
|
|
1272
|
-
f'or "view_name", got {statement.name.parts}'
|
|
1273
|
-
)
|
|
1333
|
+
project_name, view_name = match_two_part_name(
|
|
1334
|
+
statement.name, default_db_name=database_name, ensure_lower_case=isinstance(statement, CreateView)
|
|
1335
|
+
)
|
|
1274
1336
|
|
|
1275
1337
|
query_str = statement.query_str
|
|
1276
1338
|
|
|
@@ -1303,7 +1365,7 @@ class ExecuteCommands:
|
|
|
1303
1365
|
raise
|
|
1304
1366
|
elif isinstance(statement, AlterView):
|
|
1305
1367
|
try:
|
|
1306
|
-
project.update_view(view_name, query=query_str)
|
|
1368
|
+
project.update_view(view_name, query=query_str, strict_case=(not view_name.islower()))
|
|
1307
1369
|
except EntityNotExistsError:
|
|
1308
1370
|
raise ExecutorException(f"View {view_name} does not exist in {project_name}")
|
|
1309
1371
|
else:
|
|
@@ -1311,19 +1373,33 @@ class ExecuteCommands:
|
|
|
1311
1373
|
|
|
1312
1374
|
return ExecuteAnswer()
|
|
1313
1375
|
|
|
1314
|
-
def answer_drop_view(self, statement, database_name):
|
|
1315
|
-
|
|
1376
|
+
def answer_drop_view(self, statement: DropView, database_name: str) -> ExecuteAnswer:
|
|
1377
|
+
"""Drop one or more views from the specified database/project.
|
|
1316
1378
|
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
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)
|
|
1324
1400
|
|
|
1325
1401
|
try:
|
|
1326
|
-
project.drop_view(view_name)
|
|
1402
|
+
project.drop_view(view_name, strict_case=view_name_quoted)
|
|
1327
1403
|
except EntityNotExistsError:
|
|
1328
1404
|
if statement.if_exists is not True:
|
|
1329
1405
|
raise
|
|
@@ -1337,7 +1413,9 @@ class ExecuteCommands:
|
|
|
1337
1413
|
"Please pass the model parameters as a JSON object in the embedding_model field."
|
|
1338
1414
|
)
|
|
1339
1415
|
|
|
1340
|
-
project_name =
|
|
1416
|
+
project_name, kb_name = match_two_part_name(
|
|
1417
|
+
statement.name, ensure_lower_case=True, default_db_name=database_name
|
|
1418
|
+
)
|
|
1341
1419
|
|
|
1342
1420
|
if statement.storage is not None:
|
|
1343
1421
|
if len(statement.storage.parts) != 2:
|
|
@@ -1349,8 +1427,6 @@ class ExecuteCommands:
|
|
|
1349
1427
|
# TODO: implement this
|
|
1350
1428
|
raise ExecutorException("Create a knowledge base from a select is not supported yet")
|
|
1351
1429
|
|
|
1352
|
-
kb_name = statement.name.parts[-1]
|
|
1353
|
-
|
|
1354
1430
|
# create the knowledge base
|
|
1355
1431
|
_ = self.session.kb_controller.add(
|
|
1356
1432
|
name=kb_name,
|
|
@@ -1363,13 +1439,12 @@ class ExecuteCommands:
|
|
|
1363
1439
|
|
|
1364
1440
|
return ExecuteAnswer()
|
|
1365
1441
|
|
|
1366
|
-
def answer_drop_kb(self, statement: DropKnowledgeBase, database_name: str):
|
|
1367
|
-
|
|
1368
|
-
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)
|
|
1369
1444
|
|
|
1370
1445
|
# delete the knowledge base
|
|
1371
1446
|
self.session.kb_controller.delete(
|
|
1372
|
-
name=
|
|
1447
|
+
name=kb_name,
|
|
1373
1448
|
project_name=project_name,
|
|
1374
1449
|
if_exists=statement.if_exists,
|
|
1375
1450
|
)
|
|
@@ -1377,8 +1452,7 @@ class ExecuteCommands:
|
|
|
1377
1452
|
return ExecuteAnswer()
|
|
1378
1453
|
|
|
1379
1454
|
def answer_create_skill(self, statement, database_name):
|
|
1380
|
-
name = statement.name
|
|
1381
|
-
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)
|
|
1382
1456
|
|
|
1383
1457
|
try:
|
|
1384
1458
|
_ = self.session.skills_controller.add_skill(name, project_name, statement.type, statement.params)
|
|
@@ -1389,11 +1463,10 @@ class ExecuteCommands:
|
|
|
1389
1463
|
return ExecuteAnswer()
|
|
1390
1464
|
|
|
1391
1465
|
def answer_drop_skill(self, statement, database_name):
|
|
1392
|
-
name = statement.name
|
|
1393
|
-
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)
|
|
1394
1467
|
|
|
1395
1468
|
try:
|
|
1396
|
-
self.session.skills_controller.delete_skill(name, project_name)
|
|
1469
|
+
self.session.skills_controller.delete_skill(name, project_name, strict_case=True)
|
|
1397
1470
|
except ValueError as e:
|
|
1398
1471
|
# Project does not exist or skill does not exist.
|
|
1399
1472
|
raise ExecutorException(str(e))
|
|
@@ -1401,8 +1474,7 @@ class ExecuteCommands:
|
|
|
1401
1474
|
return ExecuteAnswer()
|
|
1402
1475
|
|
|
1403
1476
|
def answer_update_skill(self, statement, database_name):
|
|
1404
|
-
name = statement.name
|
|
1405
|
-
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)
|
|
1406
1478
|
|
|
1407
1479
|
type = statement.params.pop("type", None)
|
|
1408
1480
|
try:
|
|
@@ -1416,8 +1488,7 @@ class ExecuteCommands:
|
|
|
1416
1488
|
return ExecuteAnswer()
|
|
1417
1489
|
|
|
1418
1490
|
def answer_create_agent(self, statement, database_name):
|
|
1419
|
-
name = statement.name
|
|
1420
|
-
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)
|
|
1421
1492
|
|
|
1422
1493
|
skills = statement.params.pop("skills", [])
|
|
1423
1494
|
provider = statement.params.pop("provider", None)
|
|
@@ -1439,9 +1510,8 @@ class ExecuteCommands:
|
|
|
1439
1510
|
|
|
1440
1511
|
return ExecuteAnswer()
|
|
1441
1512
|
|
|
1442
|
-
def answer_drop_agent(self, statement, database_name):
|
|
1443
|
-
name = statement.name
|
|
1444
|
-
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)
|
|
1445
1515
|
|
|
1446
1516
|
try:
|
|
1447
1517
|
self.session.agents_controller.delete_agent(name, project_name)
|
|
@@ -1451,9 +1521,8 @@ class ExecuteCommands:
|
|
|
1451
1521
|
|
|
1452
1522
|
return ExecuteAnswer()
|
|
1453
1523
|
|
|
1454
|
-
def answer_update_agent(self, statement, database_name):
|
|
1455
|
-
name = statement.name
|
|
1456
|
-
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)
|
|
1457
1526
|
|
|
1458
1527
|
model = statement.params.pop("model", None)
|
|
1459
1528
|
skills_to_add = statement.params.pop("skills_to_add", [])
|
|
@@ -1474,14 +1543,13 @@ class ExecuteCommands:
|
|
|
1474
1543
|
return ExecuteAnswer()
|
|
1475
1544
|
|
|
1476
1545
|
@mark_process("learn")
|
|
1477
|
-
def answer_create_predictor(self, statement: CreatePredictor, database_name):
|
|
1478
|
-
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
|
+
)
|
|
1479
1550
|
|
|
1480
|
-
# allow creation in non-active projects, e.g. 'create mode proj.model' works whether `proj` is active or not
|
|
1481
|
-
if len(statement.name.parts) > 1:
|
|
1482
|
-
integration_name = statement.name.parts[0]
|
|
1483
|
-
model_name = statement.name.parts[-1]
|
|
1484
1551
|
statement.name.parts = [integration_name.lower(), model_name]
|
|
1552
|
+
statement.name.is_quoted = [False, False]
|
|
1485
1553
|
|
|
1486
1554
|
ml_integration_name = "lightwood" # default
|
|
1487
1555
|
if statement.using is not None:
|
|
@@ -1498,7 +1566,9 @@ class ExecuteCommands:
|
|
|
1498
1566
|
ml_handler = self.session.integration_controller.get_ml_handler(ml_integration_name)
|
|
1499
1567
|
except EntityNotExistsError:
|
|
1500
1568
|
# not exist, try to create it with same name as handler
|
|
1501
|
-
self.answer_create_ml_engine(
|
|
1569
|
+
self.answer_create_ml_engine(
|
|
1570
|
+
CreateMLEngine(name=Identifier(ml_integration_name), handler=ml_integration_name)
|
|
1571
|
+
)
|
|
1502
1572
|
|
|
1503
1573
|
ml_handler = self.session.integration_controller.get_ml_handler(ml_integration_name)
|
|
1504
1574
|
|
|
@@ -1511,7 +1581,6 @@ class ExecuteCommands:
|
|
|
1511
1581
|
|
|
1512
1582
|
try:
|
|
1513
1583
|
df = self.session.model_controller.create_model(statement, ml_handler)
|
|
1514
|
-
|
|
1515
1584
|
return ExecuteAnswer(data=ResultSet.from_df(df))
|
|
1516
1585
|
except EntityExistsError:
|
|
1517
1586
|
if getattr(statement, "if_not_exists", False) is True:
|
|
@@ -1926,22 +1995,24 @@ class ExecuteCommands:
|
|
|
1926
1995
|
self.session.model_controller.set_model_active_version(project_name, model_name, version)
|
|
1927
1996
|
return ExecuteAnswer()
|
|
1928
1997
|
|
|
1929
|
-
def answer_drop_model(self, statement, database_name):
|
|
1930
|
-
|
|
1931
|
-
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.
|
|
1932
2001
|
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
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.
|
|
1937
2005
|
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
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:
|
|
1942
2015
|
project_name = database_name
|
|
1943
|
-
else:
|
|
1944
|
-
raise ExecutorException(f"Unknown model: {statement.name}")
|
|
1945
2016
|
|
|
1946
2017
|
if version is not None:
|
|
1947
2018
|
# delete version
|
|
@@ -124,8 +124,20 @@ class ProjectDataNode(DataNode):
|
|
|
124
124
|
raise NotImplementedError(f"Can't delete object: {query_table}")
|
|
125
125
|
|
|
126
126
|
elif isinstance(query, Select):
|
|
127
|
+
match query.from_table.parts, query.from_table.is_quoted:
|
|
128
|
+
case [query_table], [is_quoted]:
|
|
129
|
+
...
|
|
130
|
+
case [query_table, int(_)], [is_quoted, _]:
|
|
131
|
+
...
|
|
132
|
+
case [query_table, str(version)], [is_quoted, _] if version.isdigit():
|
|
133
|
+
...
|
|
134
|
+
case _:
|
|
135
|
+
raise ValueError("Table name should contain only one part")
|
|
136
|
+
|
|
137
|
+
if not is_quoted:
|
|
138
|
+
query_table = query_table.lower()
|
|
139
|
+
|
|
127
140
|
# region is it query to 'models'?
|
|
128
|
-
query_table = query.from_table.parts[0].lower()
|
|
129
141
|
if query_table in ("models", "jobs", "mdb_triggers", "chatbots", "skills", "agents"):
|
|
130
142
|
new_query = deepcopy(query)
|
|
131
143
|
project_filter = BinaryOperation("=", args=[Identifier("project"), Constant(self.project.name)])
|
|
@@ -137,8 +149,7 @@ class ProjectDataNode(DataNode):
|
|
|
137
149
|
# endregion
|
|
138
150
|
|
|
139
151
|
# other table from project
|
|
140
|
-
|
|
141
|
-
if self.project.get_view(query_table):
|
|
152
|
+
if self.project.get_view(query_table, strict_case=is_quoted):
|
|
142
153
|
# this is the view
|
|
143
154
|
df = self.project.query_view(query, session)
|
|
144
155
|
|