MindsDB 25.7.3.0__py3-none-any.whl → 25.8.2.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 +11 -1
- mindsdb/api/a2a/common/server/server.py +16 -6
- mindsdb/api/executor/command_executor.py +215 -150
- 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/executor/sql_query/steps/fetch_dataframe.py +21 -24
- mindsdb/api/executor/sql_query/steps/fetch_dataframe_partition.py +9 -3
- mindsdb/api/executor/sql_query/steps/subselect_step.py +11 -8
- mindsdb/api/executor/utilities/mysql_to_duckdb_functions.py +264 -0
- mindsdb/api/executor/utilities/sql.py +30 -0
- mindsdb/api/http/initialize.py +18 -44
- 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/http/namespaces/views.py +56 -72
- 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/db2_handler/db2_handler.py +19 -23
- mindsdb/integrations/handlers/flaml_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/gong_handler/__about__.py +2 -0
- mindsdb/integrations/handlers/gong_handler/__init__.py +30 -0
- mindsdb/integrations/handlers/gong_handler/connection_args.py +37 -0
- mindsdb/integrations/handlers/gong_handler/gong_handler.py +164 -0
- mindsdb/integrations/handlers/gong_handler/gong_tables.py +508 -0
- mindsdb/integrations/handlers/gong_handler/icon.svg +25 -0
- mindsdb/integrations/handlers/gong_handler/test_gong_handler.py +125 -0
- mindsdb/integrations/handlers/google_calendar_handler/google_calendar_tables.py +82 -73
- mindsdb/integrations/handlers/hubspot_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/huggingface_handler/__init__.py +8 -12
- mindsdb/integrations/handlers/huggingface_handler/finetune.py +203 -223
- mindsdb/integrations/handlers/huggingface_handler/huggingface_handler.py +360 -383
- mindsdb/integrations/handlers/huggingface_handler/requirements.txt +7 -7
- mindsdb/integrations/handlers/huggingface_handler/requirements_cpu.txt +7 -7
- mindsdb/integrations/handlers/huggingface_handler/settings.py +25 -25
- mindsdb/integrations/handlers/langchain_handler/langchain_handler.py +83 -77
- 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/openai_handler/constants.py +11 -30
- mindsdb/integrations/handlers/openai_handler/helpers.py +27 -34
- mindsdb/integrations/handlers/openai_handler/openai_handler.py +14 -12
- 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 +215 -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/llm/config.py +0 -14
- mindsdb/integrations/libs/llm/utils.py +0 -15
- mindsdb/integrations/libs/vectordatabase_handler.py +10 -1
- mindsdb/integrations/utilities/files/file_reader.py +5 -19
- mindsdb/integrations/utilities/handler_utils.py +32 -12
- mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +1 -1
- mindsdb/interfaces/agents/agents_controller.py +246 -149
- mindsdb/interfaces/agents/constants.py +0 -1
- mindsdb/interfaces/agents/langchain_agent.py +11 -6
- 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 +174 -23
- mindsdb/interfaces/database/views.py +86 -60
- mindsdb/interfaces/jobs/jobs_controller.py +103 -110
- mindsdb/interfaces/knowledge_base/controller.py +33 -6
- mindsdb/interfaces/knowledge_base/evaluate.py +2 -1
- mindsdb/interfaces/knowledge_base/executor.py +24 -0
- mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +6 -10
- mindsdb/interfaces/knowledge_base/preprocessing/text_splitter.py +73 -0
- mindsdb/interfaces/query_context/context_controller.py +111 -145
- 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 +5 -3
- mindsdb/utilities/fs.py +54 -17
- 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.8.2.0.dist-info}/METADATA +282 -268
- {mindsdb-25.7.3.0.dist-info → mindsdb-25.8.2.0.dist-info}/RECORD +94 -92
- mindsdb/integrations/handlers/anyscale_endpoints_handler/__about__.py +0 -9
- mindsdb/integrations/handlers/anyscale_endpoints_handler/__init__.py +0 -20
- mindsdb/integrations/handlers/anyscale_endpoints_handler/anyscale_endpoints_handler.py +0 -290
- mindsdb/integrations/handlers/anyscale_endpoints_handler/creation_args.py +0 -14
- mindsdb/integrations/handlers/anyscale_endpoints_handler/icon.svg +0 -4
- mindsdb/integrations/handlers/anyscale_endpoints_handler/requirements.txt +0 -2
- mindsdb/integrations/handlers/anyscale_endpoints_handler/settings.py +0 -51
- mindsdb/integrations/handlers/anyscale_endpoints_handler/tests/test_anyscale_endpoints_handler.py +0 -212
- /mindsdb/integrations/handlers/{anyscale_endpoints_handler/tests/__init__.py → gong_handler/requirements.txt} +0 -0
- {mindsdb-25.7.3.0.dist-info → mindsdb-25.8.2.0.dist-info}/WHEEL +0 -0
- {mindsdb-25.7.3.0.dist-info → mindsdb-25.8.2.0.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.7.3.0.dist-info → mindsdb-25.8.2.0.dist-info}/top_level.txt +0 -0
|
@@ -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:
|
|
@@ -1134,11 +1195,17 @@ class ExecuteCommands:
|
|
|
1134
1195
|
msg = dedent(
|
|
1135
1196
|
f"""\
|
|
1136
1197
|
The '{handler_module_meta["name"]}' handler cannot be used. Reason is:
|
|
1137
|
-
{handler_module_meta["import"]["error_message"]}
|
|
1198
|
+
{handler_module_meta["import"]["error_message"] or msg}
|
|
1138
1199
|
"""
|
|
1139
1200
|
)
|
|
1140
1201
|
is_cloud = self.session.config.get("cloud", False)
|
|
1141
|
-
if
|
|
1202
|
+
if (
|
|
1203
|
+
is_cloud is False
|
|
1204
|
+
# NOTE: BYOM may raise these errors if there is an error in the user's code,
|
|
1205
|
+
# therefore error_message will be None
|
|
1206
|
+
and handler_module_meta["name"] != "byom"
|
|
1207
|
+
and "No module named" in handler_module_meta["import"]["error_message"]
|
|
1208
|
+
):
|
|
1142
1209
|
logger.info(get_handler_install_message(handler_module_meta["name"]))
|
|
1143
1210
|
ast_drop = DropMLEngine(name=Identifier(name))
|
|
1144
1211
|
self.answer_drop_ml_engine(ast_drop)
|
|
@@ -1147,8 +1214,21 @@ class ExecuteCommands:
|
|
|
1147
1214
|
|
|
1148
1215
|
return ExecuteAnswer()
|
|
1149
1216
|
|
|
1150
|
-
def answer_drop_ml_engine(self, statement:
|
|
1151
|
-
|
|
1217
|
+
def answer_drop_ml_engine(self, statement: DropMLEngine) -> ExecuteAnswer:
|
|
1218
|
+
"""Handles the `DROP ML_ENGINE` command, which removes an ML integration (engine) from the system.
|
|
1219
|
+
|
|
1220
|
+
Args:
|
|
1221
|
+
statement (DropMLEngine): The AST object representing the DROP ML_ENGINE command.
|
|
1222
|
+
|
|
1223
|
+
Raises:
|
|
1224
|
+
EntityNotExistsError: If the integration does not exist and IF EXISTS is not specified.
|
|
1225
|
+
ValueError: If the integration name is provided in an invalid format.
|
|
1226
|
+
|
|
1227
|
+
Returns:
|
|
1228
|
+
ExecuteAnswer: The result of the ML engine deletion operation.
|
|
1229
|
+
"""
|
|
1230
|
+
name = match_one_part_name(statement.name)
|
|
1231
|
+
|
|
1152
1232
|
integrations = self.session.integration_controller.get_all()
|
|
1153
1233
|
if name not in integrations:
|
|
1154
1234
|
if not statement.if_exists:
|
|
@@ -1158,53 +1238,57 @@ class ExecuteCommands:
|
|
|
1158
1238
|
self.session.integration_controller.delete(name)
|
|
1159
1239
|
return ExecuteAnswer()
|
|
1160
1240
|
|
|
1161
|
-
def answer_create_database(self, statement:
|
|
1162
|
-
"""
|
|
1241
|
+
def answer_create_database(self, statement: CreateDatabase) -> ExecuteAnswer:
|
|
1242
|
+
"""Create new integration or project
|
|
1243
|
+
|
|
1163
1244
|
Args:
|
|
1164
|
-
statement (
|
|
1245
|
+
statement (CreateDatabase): data for creating database/project
|
|
1246
|
+
|
|
1247
|
+
Returns:
|
|
1248
|
+
ExecuteAnswer: 'ok' answer
|
|
1165
1249
|
"""
|
|
1250
|
+
database_name = match_one_part_name(statement.name, ensure_lower_case=True)
|
|
1166
1251
|
|
|
1167
|
-
|
|
1168
|
-
raise Exception("Database name should contain only 1 part.")
|
|
1252
|
+
engine = (statement.engine or "mindsdb").lower()
|
|
1169
1253
|
|
|
1170
|
-
database_name = statement.name.parts[0]
|
|
1171
|
-
engine = statement.engine
|
|
1172
|
-
if engine is None:
|
|
1173
|
-
engine = "mindsdb"
|
|
1174
|
-
engine = engine.lower()
|
|
1175
1254
|
connection_args = statement.parameters
|
|
1176
1255
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1256
|
+
try:
|
|
1257
|
+
if engine == "mindsdb":
|
|
1179
1258
|
ProjectController().add(database_name)
|
|
1180
|
-
|
|
1181
|
-
if statement.if_not_exists is False:
|
|
1182
|
-
raise
|
|
1183
|
-
else:
|
|
1184
|
-
try:
|
|
1259
|
+
else:
|
|
1185
1260
|
self._create_integration(database_name, engine, connection_args)
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1261
|
+
except EntityExistsError:
|
|
1262
|
+
if statement.if_not_exists is False:
|
|
1263
|
+
raise
|
|
1189
1264
|
|
|
1190
1265
|
return ExecuteAnswer()
|
|
1191
1266
|
|
|
1192
|
-
def answer_drop_database(self, statement):
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1267
|
+
def answer_drop_database(self, statement: DropDatabase | DropDatasource) -> ExecuteAnswer:
|
|
1268
|
+
"""Drop a database (project or integration) by name.
|
|
1269
|
+
|
|
1270
|
+
Args:
|
|
1271
|
+
statement (DropDatabase | DropDatasource): The parsed DROP DATABASE or DROP DATASOURCE statement.
|
|
1272
|
+
|
|
1273
|
+
Raises:
|
|
1274
|
+
Exception: If the database name format is invalid.
|
|
1275
|
+
EntityNotExistsError: If the database does not exist and 'IF EXISTS' is not specified in the statement.
|
|
1276
|
+
|
|
1277
|
+
Returns:
|
|
1278
|
+
ExecuteAnswer: The result of the drop database operation.
|
|
1279
|
+
"""
|
|
1280
|
+
db_name = match_one_part_name(statement.name)
|
|
1281
|
+
|
|
1196
1282
|
try:
|
|
1197
|
-
self.session.database_controller.delete(db_name)
|
|
1283
|
+
self.session.database_controller.delete(db_name, strict_case=statement.name.is_quoted[0])
|
|
1198
1284
|
except EntityNotExistsError:
|
|
1199
1285
|
if statement.if_exists is not True:
|
|
1200
1286
|
raise
|
|
1201
1287
|
return ExecuteAnswer()
|
|
1202
1288
|
|
|
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)
|
|
1289
|
+
def answer_alter_database(self, statement: AlterDatabase) -> ExecuteAnswer:
|
|
1290
|
+
db_name = match_one_part_name(statement.name)
|
|
1291
|
+
self.session.database_controller.update(db_name, data=statement.params, strict_case=statement.name.is_quoted[0])
|
|
1208
1292
|
return ExecuteAnswer()
|
|
1209
1293
|
|
|
1210
1294
|
def answer_drop_tables(self, statement, database_name):
|
|
@@ -1242,35 +1326,19 @@ class ExecuteCommands:
|
|
|
1242
1326
|
|
|
1243
1327
|
return ExecuteAnswer()
|
|
1244
1328
|
|
|
1245
|
-
def answer_create_or_alter_view(self, statement:
|
|
1329
|
+
def answer_create_or_alter_view(self, statement: CreateView | AlterView, database_name: str) -> ExecuteAnswer:
|
|
1246
1330
|
"""Process CREATE and ALTER VIEW commands
|
|
1247
1331
|
|
|
1248
1332
|
Args:
|
|
1249
|
-
statement (
|
|
1333
|
+
statement (CreateView | AlterView): data for creating or altering view
|
|
1250
1334
|
database_name (str): name of the current database
|
|
1251
1335
|
|
|
1252
1336
|
Returns:
|
|
1253
1337
|
ExecuteAnswer: answer for the command
|
|
1254
1338
|
"""
|
|
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
|
-
)
|
|
1339
|
+
project_name, view_name = match_two_part_name(
|
|
1340
|
+
statement.name, default_db_name=database_name, ensure_lower_case=isinstance(statement, CreateView)
|
|
1341
|
+
)
|
|
1274
1342
|
|
|
1275
1343
|
query_str = statement.query_str
|
|
1276
1344
|
|
|
@@ -1280,30 +1348,18 @@ class ExecuteCommands:
|
|
|
1280
1348
|
from_table=NativeQuery(integration=statement.from_table, query=statement.query_str),
|
|
1281
1349
|
)
|
|
1282
1350
|
query_str = query.to_string()
|
|
1283
|
-
else:
|
|
1284
|
-
query = parse_sql(query_str)
|
|
1285
|
-
|
|
1286
|
-
if isinstance(query, Select):
|
|
1287
|
-
# check create view sql
|
|
1288
|
-
query.limit = Constant(1)
|
|
1289
|
-
|
|
1290
|
-
query_context_controller.set_context(query_context_controller.IGNORE_CONTEXT)
|
|
1291
|
-
try:
|
|
1292
|
-
SQLQuery(query, session=self.session, database=database_name)
|
|
1293
|
-
finally:
|
|
1294
|
-
query_context_controller.release_context(query_context_controller.IGNORE_CONTEXT)
|
|
1295
1351
|
|
|
1296
1352
|
project = self.session.database_controller.get_project(project_name)
|
|
1297
1353
|
|
|
1298
1354
|
if isinstance(statement, CreateView):
|
|
1299
1355
|
try:
|
|
1300
|
-
project.create_view(view_name, query=query_str)
|
|
1356
|
+
project.create_view(view_name, query=query_str, session=self.session)
|
|
1301
1357
|
except EntityExistsError:
|
|
1302
1358
|
if getattr(statement, "if_not_exists", False) is False:
|
|
1303
1359
|
raise
|
|
1304
1360
|
elif isinstance(statement, AlterView):
|
|
1305
1361
|
try:
|
|
1306
|
-
project.update_view(view_name, query=query_str)
|
|
1362
|
+
project.update_view(view_name, query=query_str, strict_case=(not view_name.islower()))
|
|
1307
1363
|
except EntityNotExistsError:
|
|
1308
1364
|
raise ExecutorException(f"View {view_name} does not exist in {project_name}")
|
|
1309
1365
|
else:
|
|
@@ -1311,19 +1367,33 @@ class ExecuteCommands:
|
|
|
1311
1367
|
|
|
1312
1368
|
return ExecuteAnswer()
|
|
1313
1369
|
|
|
1314
|
-
def answer_drop_view(self, statement, database_name):
|
|
1315
|
-
|
|
1370
|
+
def answer_drop_view(self, statement: DropView, database_name: str) -> ExecuteAnswer:
|
|
1371
|
+
"""Drop one or more views from the specified database/project.
|
|
1316
1372
|
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1373
|
+
Args:
|
|
1374
|
+
statement (DropView): The parsed DROP VIEW statement containing view names and options.
|
|
1375
|
+
database_name (str): The name of the database (project) from which to drop the views.
|
|
1376
|
+
|
|
1377
|
+
Raises:
|
|
1378
|
+
EntityNotExistsError: If a view does not exist and 'IF EXISTS' is not specified in the statement.
|
|
1379
|
+
ValueError: If the view name format is invalid.
|
|
1380
|
+
|
|
1381
|
+
Returns:
|
|
1382
|
+
ExecuteAnswer: The result of the drop view operation.
|
|
1383
|
+
"""
|
|
1384
|
+
for name in statement.names:
|
|
1385
|
+
match name.parts, name.is_quoted:
|
|
1386
|
+
case [view_name], [view_name_quoted]:
|
|
1387
|
+
db_name_quoted = False
|
|
1388
|
+
case [database_name, view_name], [db_name_quoted, view_name_quoted]:
|
|
1389
|
+
pass
|
|
1390
|
+
case _:
|
|
1391
|
+
raise ValueError(f"Invalid view name: {name}")
|
|
1392
|
+
|
|
1393
|
+
project = self.session.database_controller.get_project(database_name, db_name_quoted)
|
|
1324
1394
|
|
|
1325
1395
|
try:
|
|
1326
|
-
project.drop_view(view_name)
|
|
1396
|
+
project.drop_view(view_name, strict_case=view_name_quoted)
|
|
1327
1397
|
except EntityNotExistsError:
|
|
1328
1398
|
if statement.if_exists is not True:
|
|
1329
1399
|
raise
|
|
@@ -1337,7 +1407,9 @@ class ExecuteCommands:
|
|
|
1337
1407
|
"Please pass the model parameters as a JSON object in the embedding_model field."
|
|
1338
1408
|
)
|
|
1339
1409
|
|
|
1340
|
-
project_name =
|
|
1410
|
+
project_name, kb_name = match_two_part_name(
|
|
1411
|
+
statement.name, ensure_lower_case=True, default_db_name=database_name
|
|
1412
|
+
)
|
|
1341
1413
|
|
|
1342
1414
|
if statement.storage is not None:
|
|
1343
1415
|
if len(statement.storage.parts) != 2:
|
|
@@ -1349,8 +1421,6 @@ class ExecuteCommands:
|
|
|
1349
1421
|
# TODO: implement this
|
|
1350
1422
|
raise ExecutorException("Create a knowledge base from a select is not supported yet")
|
|
1351
1423
|
|
|
1352
|
-
kb_name = statement.name.parts[-1]
|
|
1353
|
-
|
|
1354
1424
|
# create the knowledge base
|
|
1355
1425
|
_ = self.session.kb_controller.add(
|
|
1356
1426
|
name=kb_name,
|
|
@@ -1363,13 +1433,12 @@ class ExecuteCommands:
|
|
|
1363
1433
|
|
|
1364
1434
|
return ExecuteAnswer()
|
|
1365
1435
|
|
|
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
|
|
1436
|
+
def answer_drop_kb(self, statement: DropKnowledgeBase, database_name: str) -> ExecuteAnswer:
|
|
1437
|
+
project_name, kb_name = match_two_part_name(statement.name, default_db_name=database_name)
|
|
1369
1438
|
|
|
1370
1439
|
# delete the knowledge base
|
|
1371
1440
|
self.session.kb_controller.delete(
|
|
1372
|
-
name=
|
|
1441
|
+
name=kb_name,
|
|
1373
1442
|
project_name=project_name,
|
|
1374
1443
|
if_exists=statement.if_exists,
|
|
1375
1444
|
)
|
|
@@ -1377,8 +1446,7 @@ class ExecuteCommands:
|
|
|
1377
1446
|
return ExecuteAnswer()
|
|
1378
1447
|
|
|
1379
1448
|
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
|
|
1449
|
+
project_name, name = match_two_part_name(statement.name, ensure_lower_case=True, default_db_name=database_name)
|
|
1382
1450
|
|
|
1383
1451
|
try:
|
|
1384
1452
|
_ = self.session.skills_controller.add_skill(name, project_name, statement.type, statement.params)
|
|
@@ -1389,11 +1457,10 @@ class ExecuteCommands:
|
|
|
1389
1457
|
return ExecuteAnswer()
|
|
1390
1458
|
|
|
1391
1459
|
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
|
|
1460
|
+
project_name, name = match_two_part_name(statement.name, default_db_name=database_name)
|
|
1394
1461
|
|
|
1395
1462
|
try:
|
|
1396
|
-
self.session.skills_controller.delete_skill(name, project_name)
|
|
1463
|
+
self.session.skills_controller.delete_skill(name, project_name, strict_case=True)
|
|
1397
1464
|
except ValueError as e:
|
|
1398
1465
|
# Project does not exist or skill does not exist.
|
|
1399
1466
|
raise ExecutorException(str(e))
|
|
@@ -1401,8 +1468,7 @@ class ExecuteCommands:
|
|
|
1401
1468
|
return ExecuteAnswer()
|
|
1402
1469
|
|
|
1403
1470
|
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
|
|
1471
|
+
project_name, name = match_two_part_name(statement.name, default_db_name=database_name)
|
|
1406
1472
|
|
|
1407
1473
|
type = statement.params.pop("type", None)
|
|
1408
1474
|
try:
|
|
@@ -1416,8 +1482,7 @@ class ExecuteCommands:
|
|
|
1416
1482
|
return ExecuteAnswer()
|
|
1417
1483
|
|
|
1418
1484
|
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
|
|
1485
|
+
project_name, name = match_two_part_name(statement.name, ensure_lower_case=True, default_db_name=database_name)
|
|
1421
1486
|
|
|
1422
1487
|
skills = statement.params.pop("skills", [])
|
|
1423
1488
|
provider = statement.params.pop("provider", None)
|
|
@@ -1439,9 +1504,8 @@ class ExecuteCommands:
|
|
|
1439
1504
|
|
|
1440
1505
|
return ExecuteAnswer()
|
|
1441
1506
|
|
|
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
|
|
1507
|
+
def answer_drop_agent(self, statement: DropAgent, database_name: str):
|
|
1508
|
+
project_name, name = match_two_part_name(statement.name, default_db_name=database_name)
|
|
1445
1509
|
|
|
1446
1510
|
try:
|
|
1447
1511
|
self.session.agents_controller.delete_agent(name, project_name)
|
|
@@ -1451,9 +1515,8 @@ class ExecuteCommands:
|
|
|
1451
1515
|
|
|
1452
1516
|
return ExecuteAnswer()
|
|
1453
1517
|
|
|
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
|
|
1518
|
+
def answer_update_agent(self, statement: UpdateAgent, database_name: str):
|
|
1519
|
+
project_name, name = match_two_part_name(statement.name, default_db_name=database_name)
|
|
1457
1520
|
|
|
1458
1521
|
model = statement.params.pop("model", None)
|
|
1459
1522
|
skills_to_add = statement.params.pop("skills_to_add", [])
|
|
@@ -1474,14 +1537,13 @@ class ExecuteCommands:
|
|
|
1474
1537
|
return ExecuteAnswer()
|
|
1475
1538
|
|
|
1476
1539
|
@mark_process("learn")
|
|
1477
|
-
def answer_create_predictor(self, statement: CreatePredictor, database_name):
|
|
1478
|
-
integration_name =
|
|
1540
|
+
def answer_create_predictor(self, statement: CreatePredictor, database_name: str):
|
|
1541
|
+
integration_name, model_name = match_two_part_name(
|
|
1542
|
+
statement.name, ensure_lower_case=True, default_db_name=database_name
|
|
1543
|
+
)
|
|
1479
1544
|
|
|
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
1545
|
statement.name.parts = [integration_name.lower(), model_name]
|
|
1546
|
+
statement.name.is_quoted = [False, False]
|
|
1485
1547
|
|
|
1486
1548
|
ml_integration_name = "lightwood" # default
|
|
1487
1549
|
if statement.using is not None:
|
|
@@ -1498,7 +1560,9 @@ class ExecuteCommands:
|
|
|
1498
1560
|
ml_handler = self.session.integration_controller.get_ml_handler(ml_integration_name)
|
|
1499
1561
|
except EntityNotExistsError:
|
|
1500
1562
|
# not exist, try to create it with same name as handler
|
|
1501
|
-
self.answer_create_ml_engine(
|
|
1563
|
+
self.answer_create_ml_engine(
|
|
1564
|
+
CreateMLEngine(name=Identifier(ml_integration_name), handler=ml_integration_name)
|
|
1565
|
+
)
|
|
1502
1566
|
|
|
1503
1567
|
ml_handler = self.session.integration_controller.get_ml_handler(ml_integration_name)
|
|
1504
1568
|
|
|
@@ -1511,7 +1575,6 @@ class ExecuteCommands:
|
|
|
1511
1575
|
|
|
1512
1576
|
try:
|
|
1513
1577
|
df = self.session.model_controller.create_model(statement, ml_handler)
|
|
1514
|
-
|
|
1515
1578
|
return ExecuteAnswer(data=ResultSet.from_df(df))
|
|
1516
1579
|
except EntityExistsError:
|
|
1517
1580
|
if getattr(statement, "if_not_exists", False) is True:
|
|
@@ -1926,22 +1989,24 @@ class ExecuteCommands:
|
|
|
1926
1989
|
self.session.model_controller.set_model_active_version(project_name, model_name, version)
|
|
1927
1990
|
return ExecuteAnswer()
|
|
1928
1991
|
|
|
1929
|
-
def answer_drop_model(self, statement, database_name):
|
|
1930
|
-
|
|
1931
|
-
version
|
|
1992
|
+
def answer_drop_model(self, statement: DropPredictor, database_name: str) -> ExecuteAnswer:
|
|
1993
|
+
"""Handles the DROP MODEL (or DROP PREDICTOR) command, which removes a model
|
|
1994
|
+
or a specific model version from a project.
|
|
1932
1995
|
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
model_parts = model_parts[:-1]
|
|
1996
|
+
Args:
|
|
1997
|
+
statement (DropPredictor): The AST object representing the DROP MODEL or DROP PREDICTOR command.
|
|
1998
|
+
database_name (str): The name of the current database/project.
|
|
1937
1999
|
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
2000
|
+
Raises:
|
|
2001
|
+
EntityNotExistsError: If the model or version does not exist and IF EXISTS is not specified.
|
|
2002
|
+
ValueError: If the model name format is invalid.
|
|
2003
|
+
|
|
2004
|
+
Returns:
|
|
2005
|
+
ExecuteAnswer: The result of the model deletion operation.
|
|
2006
|
+
"""
|
|
2007
|
+
project_name, model_name, version = resolve_model_identifier(statement.name)
|
|
2008
|
+
if project_name is None:
|
|
1942
2009
|
project_name = database_name
|
|
1943
|
-
else:
|
|
1944
|
-
raise ExecutorException(f"Unknown model: {statement.name}")
|
|
1945
2010
|
|
|
1946
2011
|
if version is not None:
|
|
1947
2012
|
# 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
|
|
|
@@ -154,6 +154,7 @@ class PlanJoinTablesQuery:
|
|
|
154
154
|
if len(table.parts) > 0:
|
|
155
155
|
if table.parts[0] in self.planner.databases:
|
|
156
156
|
integration = table.parts.pop(0)
|
|
157
|
+
table.is_quoted.pop(0)
|
|
157
158
|
else:
|
|
158
159
|
integration = self.planner.default_namespace
|
|
159
160
|
|
|
@@ -389,6 +390,7 @@ class PlanJoinTablesQuery:
|
|
|
389
390
|
def process_table(self, item, query_in):
|
|
390
391
|
table = copy.deepcopy(item.table)
|
|
391
392
|
table.parts.insert(0, item.integration)
|
|
393
|
+
table.is_quoted.insert(0, False)
|
|
392
394
|
query2 = Select(from_table=table, targets=[Star()])
|
|
393
395
|
# parts = tuple(map(str.lower, table_name.parts))
|
|
394
396
|
conditions = item.conditions
|
|
@@ -410,6 +412,7 @@ class PlanJoinTablesQuery:
|
|
|
410
412
|
break
|
|
411
413
|
col = copy.deepcopy(col)
|
|
412
414
|
col.field.parts = [col.field.parts[-1]]
|
|
415
|
+
col.field.is_quoted = [col.field.is_quoted[-1]]
|
|
413
416
|
order_by.append(col)
|
|
414
417
|
|
|
415
418
|
if order_by is not False:
|