MindsDB 25.4.1.0__py3-none-any.whl → 25.4.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of MindsDB might be problematic. Click here for more details.

Files changed (63) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/api/executor/command_executor.py +91 -61
  3. mindsdb/api/executor/data_types/answer.py +9 -12
  4. mindsdb/api/executor/datahub/classes/response.py +11 -0
  5. mindsdb/api/executor/datahub/datanodes/datanode.py +4 -4
  6. mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +10 -11
  7. mindsdb/api/executor/datahub/datanodes/integration_datanode.py +22 -16
  8. mindsdb/api/executor/datahub/datanodes/mindsdb_tables.py +43 -1
  9. mindsdb/api/executor/datahub/datanodes/project_datanode.py +20 -20
  10. mindsdb/api/executor/planner/plan_join.py +2 -2
  11. mindsdb/api/executor/planner/query_plan.py +1 -0
  12. mindsdb/api/executor/planner/query_planner.py +86 -14
  13. mindsdb/api/executor/planner/steps.py +11 -2
  14. mindsdb/api/executor/sql_query/result_set.py +10 -7
  15. mindsdb/api/executor/sql_query/sql_query.py +69 -84
  16. mindsdb/api/executor/sql_query/steps/__init__.py +1 -0
  17. mindsdb/api/executor/sql_query/steps/delete_step.py +2 -3
  18. mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +5 -3
  19. mindsdb/api/executor/sql_query/steps/fetch_dataframe_partition.py +288 -0
  20. mindsdb/api/executor/sql_query/steps/insert_step.py +2 -2
  21. mindsdb/api/executor/sql_query/steps/prepare_steps.py +2 -2
  22. mindsdb/api/executor/sql_query/steps/subselect_step.py +20 -8
  23. mindsdb/api/executor/sql_query/steps/update_step.py +4 -6
  24. mindsdb/api/http/namespaces/sql.py +4 -1
  25. mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/ok_packet.py +1 -1
  26. mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +4 -27
  27. mindsdb/api/mysql/mysql_proxy/libs/constants/mysql.py +1 -0
  28. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +38 -37
  29. mindsdb/integrations/handlers/chromadb_handler/chromadb_handler.py +23 -13
  30. mindsdb/integrations/handlers/langchain_embedding_handler/langchain_embedding_handler.py +17 -16
  31. mindsdb/integrations/handlers/langchain_handler/langchain_handler.py +1 -0
  32. mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +1 -1
  33. mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +3 -2
  34. mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +4 -4
  35. mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +26 -16
  36. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +36 -7
  37. mindsdb/integrations/handlers/redshift_handler/redshift_handler.py +1 -1
  38. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +18 -11
  39. mindsdb/integrations/libs/llm/config.py +11 -1
  40. mindsdb/integrations/libs/llm/utils.py +12 -0
  41. mindsdb/integrations/libs/ml_handler_process/learn_process.py +1 -2
  42. mindsdb/integrations/libs/response.py +9 -4
  43. mindsdb/integrations/libs/vectordatabase_handler.py +17 -5
  44. mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +8 -98
  45. mindsdb/interfaces/agents/constants.py +12 -1
  46. mindsdb/interfaces/agents/langchain_agent.py +6 -0
  47. mindsdb/interfaces/database/log.py +8 -9
  48. mindsdb/interfaces/database/projects.py +1 -5
  49. mindsdb/interfaces/functions/controller.py +59 -17
  50. mindsdb/interfaces/functions/to_markdown.py +194 -0
  51. mindsdb/interfaces/jobs/jobs_controller.py +3 -3
  52. mindsdb/interfaces/knowledge_base/controller.py +223 -97
  53. mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +3 -14
  54. mindsdb/interfaces/query_context/context_controller.py +224 -1
  55. mindsdb/interfaces/storage/db.py +23 -0
  56. mindsdb/migrations/versions/2025-03-21_fda503400e43_queries.py +45 -0
  57. mindsdb/utilities/context_executor.py +1 -1
  58. mindsdb/utilities/partitioning.py +35 -20
  59. {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.1.dist-info}/METADATA +227 -224
  60. {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.1.dist-info}/RECORD +63 -59
  61. {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.1.dist-info}/WHEEL +0 -0
  62. {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.1.dist-info}/licenses/LICENSE +0 -0
  63. {mindsdb-25.4.1.0.dist-info → mindsdb-25.4.2.1.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.4.1.0'
3
+ __version__ = '25.4.2.1'
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'
@@ -34,6 +34,7 @@ from mindsdb_sql_parser.ast import (
34
34
  Update,
35
35
  Use,
36
36
  Tuple,
37
+ Function,
37
38
  )
38
39
 
39
40
  # typed models
@@ -164,18 +165,17 @@ class ExecuteCommands:
164
165
  self.datahub = session.datahub
165
166
 
166
167
  @profiler.profile()
167
- def execute_command(self, statement, database_name: str = None) -> ExecuteAnswer:
168
- sql = None
169
- if isinstance(statement, ASTNode):
170
- sql = statement.to_string()
171
- sql_lower = sql.lower()
168
+ def execute_command(self, statement: ASTNode, database_name: str = None) -> ExecuteAnswer:
169
+ sql: str = statement.to_string()
170
+ sql_lower: str = sql.lower()
172
171
 
173
172
  if database_name is None:
174
173
  database_name = self.session.database
175
174
 
176
- if type(statement) is CreateDatabase:
175
+ statement_type = type(statement)
176
+ if statement_type is CreateDatabase:
177
177
  return self.answer_create_database(statement)
178
- elif type(statement) is CreateMLEngine:
178
+ elif statement_type is CreateMLEngine:
179
179
  name = statement.name.parts[-1]
180
180
 
181
181
  return self.answer_create_ml_engine(
@@ -184,16 +184,16 @@ class ExecuteCommands:
184
184
  params=statement.params,
185
185
  if_not_exists=getattr(statement, "if_not_exists", False)
186
186
  )
187
- elif type(statement) is DropMLEngine:
187
+ elif statement_type is DropMLEngine:
188
188
  return self.answer_drop_ml_engine(statement)
189
- elif type(statement) is DropPredictor:
189
+ elif statement_type is DropPredictor:
190
190
  return self.answer_drop_model(statement, database_name)
191
191
 
192
- elif type(statement) is DropTables:
192
+ elif statement_type is DropTables:
193
193
  return self.answer_drop_tables(statement, database_name)
194
- elif type(statement) is DropDatasource or type(statement) is DropDatabase:
194
+ elif statement_type is DropDatasource or statement_type is DropDatabase:
195
195
  return self.answer_drop_database(statement)
196
- elif type(statement) is Describe:
196
+ elif statement_type is Describe:
197
197
  # NOTE in sql 'describe table' is same as 'show columns'
198
198
  obj_type = statement.type
199
199
 
@@ -202,11 +202,11 @@ class ExecuteCommands:
202
202
  else:
203
203
  return self.answer_describe_object(obj_type.upper(), statement.value, database_name)
204
204
 
205
- elif type(statement) is RetrainPredictor:
205
+ elif statement_type is RetrainPredictor:
206
206
  return self.answer_retrain_predictor(statement, database_name)
207
- elif type(statement) is FinetunePredictor:
207
+ elif statement_type is FinetunePredictor:
208
208
  return self.answer_finetune_predictor(statement, database_name)
209
- elif type(statement) is Show:
209
+ elif statement_type is Show:
210
210
  sql_category = statement.category.lower()
211
211
  if hasattr(statement, "modes"):
212
212
  if isinstance(statement.modes, list) is False:
@@ -504,13 +504,13 @@ class ExecuteCommands:
504
504
  return self.answer_select(query)
505
505
  else:
506
506
  raise NotSupportedYet(f"Statement not implemented: {sql}")
507
- elif type(statement) in (
507
+ elif statement_type in (
508
508
  StartTransaction,
509
509
  CommitTransaction,
510
510
  RollbackTransaction,
511
511
  ):
512
512
  return ExecuteAnswer()
513
- elif type(statement) is Set:
513
+ elif statement_type is Set:
514
514
  category = (statement.category or "").lower()
515
515
  if category == "" and isinstance(statement.name, Identifier):
516
516
  param = statement.name.parts[0].lower()
@@ -565,85 +565,118 @@ class ExecuteCommands:
565
565
  f"SQL statement is not processable, return OK package: {sql}"
566
566
  )
567
567
  return ExecuteAnswer()
568
- elif type(statement) is Use:
568
+ elif statement_type is Use:
569
569
  db_name = statement.value.parts[-1]
570
570
  self.change_default_db(db_name)
571
571
  return ExecuteAnswer()
572
- elif type(statement) in (
572
+ elif statement_type in (
573
573
  CreatePredictor,
574
574
  CreateAnomalyDetectionModel, # we may want to specialize these in the future
575
575
  ):
576
576
  return self.answer_create_predictor(statement, database_name)
577
- elif type(statement) is CreateView:
577
+ elif statement_type is CreateView:
578
578
  return self.answer_create_view(statement, database_name)
579
- elif type(statement) is DropView:
579
+ elif statement_type is DropView:
580
580
  return self.answer_drop_view(statement, database_name)
581
- elif type(statement) is Delete:
582
- SQLQuery(statement, session=self.session, execute=True, database=database_name)
583
- return ExecuteAnswer()
584
-
585
- elif type(statement) is Insert:
586
- SQLQuery(statement, session=self.session, execute=True, database=database_name)
587
- return ExecuteAnswer()
588
- elif type(statement) is Update:
589
- SQLQuery(statement, session=self.session, execute=True, database=database_name)
590
- return ExecuteAnswer()
581
+ elif statement_type is Delete:
582
+ query = SQLQuery(statement, session=self.session, database=database_name)
583
+ return ExecuteAnswer(
584
+ affected_rows=query.fetched_data.affected_rows
585
+ )
586
+ elif statement_type is Insert:
587
+ query = SQLQuery(statement, session=self.session, database=database_name)
588
+ return ExecuteAnswer(
589
+ affected_rows=query.fetched_data.affected_rows
590
+ )
591
+ elif statement_type is Update:
592
+ query = SQLQuery(statement, session=self.session, database=database_name)
593
+ return ExecuteAnswer(
594
+ affected_rows=query.fetched_data.affected_rows
595
+ )
591
596
  elif (
592
- type(statement) is Alter
597
+ statement_type is Alter
593
598
  and ("disable keys" in sql_lower)
594
599
  or ("enable keys" in sql_lower)
595
600
  ):
596
601
  return ExecuteAnswer()
597
- elif type(statement) is Select:
602
+ elif statement_type is Select:
603
+ ret = self.exec_service_function(statement, database_name)
604
+ if ret is not None:
605
+ return ret
598
606
  query = SQLQuery(statement, session=self.session, database=database_name)
599
607
  return self.answer_select(query)
600
- elif type(statement) is Union:
608
+ elif statement_type is Union:
601
609
  query = SQLQuery(statement, session=self.session, database=database_name)
602
610
  return self.answer_select(query)
603
- elif type(statement) is Explain:
611
+ elif statement_type is Explain:
604
612
  return self.answer_show_columns(statement.target, database_name=database_name)
605
- elif type(statement) is CreateTable:
613
+ elif statement_type is CreateTable:
606
614
  return self.answer_create_table(statement, database_name)
607
615
  # -- jobs --
608
- elif type(statement) is CreateJob:
616
+ elif statement_type is CreateJob:
609
617
  return self.answer_create_job(statement, database_name)
610
- elif type(statement) is DropJob:
618
+ elif statement_type is DropJob:
611
619
  return self.answer_drop_job(statement, database_name)
612
620
  # -- triggers --
613
- elif type(statement) is CreateTrigger:
621
+ elif statement_type is CreateTrigger:
614
622
  return self.answer_create_trigger(statement, database_name)
615
- elif type(statement) is DropTrigger:
623
+ elif statement_type is DropTrigger:
616
624
  return self.answer_drop_trigger(statement, database_name)
617
625
  # -- chatbots
618
- elif type(statement) is CreateChatBot:
626
+ elif statement_type is CreateChatBot:
619
627
  return self.answer_create_chatbot(statement, database_name)
620
- elif type(statement) is UpdateChatBot:
628
+ elif statement_type is UpdateChatBot:
621
629
  return self.answer_update_chatbot(statement, database_name)
622
- elif type(statement) is DropChatBot:
630
+ elif statement_type is DropChatBot:
623
631
  return self.answer_drop_chatbot(statement, database_name)
624
- elif type(statement) is CreateKnowledgeBase:
632
+ elif statement_type is CreateKnowledgeBase:
625
633
  return self.answer_create_kb(statement, database_name)
626
- elif type(statement) is DropKnowledgeBase:
634
+ elif statement_type is DropKnowledgeBase:
627
635
  return self.answer_drop_kb(statement, database_name)
628
- elif type(statement) is CreateSkill:
636
+ elif statement_type is CreateSkill:
629
637
  return self.answer_create_skill(statement, database_name)
630
- elif type(statement) is DropSkill:
638
+ elif statement_type is DropSkill:
631
639
  return self.answer_drop_skill(statement, database_name)
632
- elif type(statement) is UpdateSkill:
640
+ elif statement_type is UpdateSkill:
633
641
  return self.answer_update_skill(statement, database_name)
634
- elif type(statement) is CreateAgent:
642
+ elif statement_type is CreateAgent:
635
643
  return self.answer_create_agent(statement, database_name)
636
- elif type(statement) is DropAgent:
644
+ elif statement_type is DropAgent:
637
645
  return self.answer_drop_agent(statement, database_name)
638
- elif type(statement) is UpdateAgent:
646
+ elif statement_type is UpdateAgent:
639
647
  return self.answer_update_agent(statement, database_name)
640
- elif type(statement) is Evaluate:
648
+ elif statement_type is Evaluate:
641
649
  statement.data = parse_sql(statement.query_str)
642
650
  return self.answer_evaluate_metric(statement, database_name)
643
651
  else:
644
652
  logger.warning(f"Unknown SQL statement: {sql}")
645
653
  raise NotSupportedYet(f"Unknown SQL statement: {sql}")
646
654
 
655
+ def exec_service_function(self, statement: Select, database_name: str) -> Optional[ExecuteAnswer]:
656
+ """
657
+ If input query is a single line select without FROM
658
+ and has function in targets that matches with one of the mindsdb service functions:
659
+ - execute this function and return response
660
+ Otherwise, return None to allow to continue execution query outside
661
+ """
662
+
663
+ if statement.from_table is not None or len(statement.targets) != 1:
664
+ return
665
+
666
+ target = statement.targets[0]
667
+ if not isinstance(target, Function):
668
+ return
669
+
670
+ command = target.op.lower()
671
+ args = [arg.value for arg in target.args if isinstance(arg, Constant)]
672
+ if command == 'query_resume':
673
+ ret = SQLQuery(None, session=self.session, database=database_name, query_id=args[0])
674
+ return self.answer_select(ret)
675
+
676
+ elif command == 'query_cancel':
677
+ query_context_controller.cancel_query(*args)
678
+ return ExecuteAnswer()
679
+
647
680
  def answer_create_trigger(self, statement, database_name):
648
681
  triggers_controller = TriggersController()
649
682
 
@@ -785,8 +818,7 @@ class ExecuteCommands:
785
818
  raise Exception(
786
819
  f'Nested query failed to execute with error: "{e}", please check and try again.'
787
820
  )
788
- result = sqlquery.fetch('dataframe')
789
- df = result["result"]
821
+ df = sqlquery.fetched_data.to_df()
790
822
  df.columns = [
791
823
  str(t.alias) if hasattr(t, "alias") else str(t.parts[-1])
792
824
  for t in statement.data.targets
@@ -1253,7 +1285,6 @@ class ExecuteCommands:
1253
1285
  project_name = parts[0]
1254
1286
 
1255
1287
  query_str = statement.query_str
1256
- query = parse_sql(query_str)
1257
1288
 
1258
1289
  if isinstance(statement.from_table, Identifier):
1259
1290
  query = Select(
@@ -1263,6 +1294,8 @@ class ExecuteCommands:
1263
1294
  ),
1264
1295
  )
1265
1296
  query_str = str(query)
1297
+ else:
1298
+ query = parse_sql(query_str)
1266
1299
 
1267
1300
  if isinstance(query, Select):
1268
1301
  # check create view sql
@@ -1272,9 +1305,7 @@ class ExecuteCommands:
1272
1305
  query_context_controller.IGNORE_CONTEXT
1273
1306
  )
1274
1307
  try:
1275
- sqlquery = SQLQuery(query, session=self.session, database=database_name)
1276
- if sqlquery.fetch()["success"] is not True:
1277
- raise ExecutorException("Wrong view query")
1308
+ SQLQuery(query, session=self.session, database=database_name)
1278
1309
  finally:
1279
1310
  query_context_controller.release_context(
1280
1311
  query_context_controller.IGNORE_CONTEXT
@@ -1920,9 +1951,8 @@ class ExecuteCommands:
1920
1951
  return ExecuteAnswer()
1921
1952
 
1922
1953
  def answer_select(self, query):
1923
- data = query.fetch()
1924
-
1925
- return ExecuteAnswer(data=data["result"])
1954
+ data = query.fetched_data
1955
+ return ExecuteAnswer(data=data)
1926
1956
 
1927
1957
  def answer_update_model_version(self, model_version, database_name):
1928
1958
  if not isinstance(model_version, Identifier):
@@ -1,16 +1,13 @@
1
- from typing import List
1
+ from dataclasses import dataclass
2
+ from typing import List, Optional
3
+
2
4
  from mindsdb.api.executor.sql_query.result_set import ResultSet
3
5
 
4
6
 
7
+ @dataclass(kw_only=True, slots=True)
5
8
  class ExecuteAnswer:
6
- def __init__(
7
- self,
8
- data: ResultSet = None,
9
- state_track: List[List] = None,
10
- error_code: int = None,
11
- error_message: str = None,
12
- ):
13
- self.data = data
14
- self.state_track = state_track
15
- self.error_code = error_code
16
- self.error_message = error_message
9
+ data: Optional[ResultSet] = None
10
+ state_track: Optional[List[List]] = None
11
+ error_code: Optional[int] = None
12
+ error_message: Optional[str] = None
13
+ affected_rows: Optional[int] = None
@@ -0,0 +1,11 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Optional, List, Dict
3
+
4
+ import pandas as pd
5
+
6
+
7
+ @dataclass
8
+ class DataHubResponse:
9
+ data_frame: pd.DataFrame = field(default_factory=pd.DataFrame)
10
+ columns: List[Dict] = field(default_factory=list)
11
+ affected_rows: Optional[int] = None
@@ -1,3 +1,6 @@
1
+ from mindsdb.api.executor.datahub.classes.response import DataHubResponse
2
+
3
+
1
4
  class DataNode:
2
5
  type = 'meta'
3
6
 
@@ -10,11 +13,8 @@ class DataNode:
10
13
  def get_tables(self):
11
14
  pass
12
15
 
13
- def has_table(self, tableName):
14
- pass
15
-
16
16
  def get_table_columns(self, tableName, schema_name=None):
17
17
  pass
18
18
 
19
- def query(self, query=None, native_query=None, session=None):
19
+ def query(self, query=None, native_query=None, session=None) -> DataHubResponse:
20
20
  return []
@@ -9,7 +9,7 @@ from mindsdb.api.executor import exceptions as exc
9
9
  from mindsdb.api.executor.utilities.sql import query_df
10
10
  from mindsdb.api.executor.utilities.sql import get_query_tables
11
11
  from mindsdb.interfaces.database.projects import ProjectController
12
-
12
+ from mindsdb.api.executor.datahub.classes.response import DataHubResponse
13
13
  from mindsdb.utilities import log
14
14
 
15
15
  from .system_tables import (
@@ -17,7 +17,7 @@ from .system_tables import (
17
17
  PluginsTable, EnginesTable, KeyColumnUsageTable, StatisticsTable,
18
18
  CharacterSetsTable, CollationsTable)
19
19
  from .mindsdb_tables import (
20
- ModelsTable, DatabasesTable, MLEnginesTable, HandlersTable, JobsTable,
20
+ ModelsTable, DatabasesTable, MLEnginesTable, HandlersTable, JobsTable, QueriesTable,
21
21
  ChatbotsTable, KBTable, SkillsTable, AgentsTable, ViewsTable, TriggersTable)
22
22
 
23
23
 
@@ -32,7 +32,8 @@ class InformationSchemaDataNode(DataNode):
32
32
  PluginsTable, EnginesTable, KeyColumnUsageTable, StatisticsTable,
33
33
  CharacterSetsTable, CollationsTable,
34
34
  ModelsTable, DatabasesTable, MLEnginesTable, HandlersTable, JobsTable,
35
- ChatbotsTable, KBTable, SkillsTable, AgentsTable, ViewsTable, TriggersTable
35
+ ChatbotsTable, KBTable, SkillsTable, AgentsTable, ViewsTable, TriggersTable,
36
+ QueriesTable
36
37
  ]
37
38
 
38
39
  def __init__(self, session):
@@ -110,12 +111,6 @@ class InformationSchemaDataNode(DataNode):
110
111
 
111
112
  return None
112
113
 
113
- def has_table(self, tableName):
114
- tn = tableName.upper()
115
- if tn in self.tables:
116
- return True
117
- return False
118
-
119
114
  def get_table_columns(self, tableName, schema_name=None):
120
115
  tn = tableName.upper()
121
116
  if tn in self.tables:
@@ -143,7 +138,7 @@ class InformationSchemaDataNode(DataNode):
143
138
  if table.visible
144
139
  }
145
140
 
146
- def query(self, query: ASTNode, session=None):
141
+ def query(self, query: ASTNode, session=None) -> DataHubResponse:
147
142
  query_tables = [x[1] for x in get_query_tables(query)]
148
143
 
149
144
  if len(query_tables) != 1:
@@ -166,7 +161,11 @@ class InformationSchemaDataNode(DataNode):
166
161
 
167
162
  columns_info = [{"name": k, "type": v} for k, v in data.dtypes.items()]
168
163
 
169
- return data, columns_info
164
+ return DataHubResponse(
165
+ data_frame=data,
166
+ columns=columns_info,
167
+ affected_rows=0
168
+ )
170
169
 
171
170
  def _get_empty_table(self, table):
172
171
  columns = table.columns
@@ -10,16 +10,19 @@ from sqlalchemy.types import (
10
10
  Integer, Float, Text
11
11
  )
12
12
 
13
+ from mindsdb_sql_parser.ast.base import ASTNode
13
14
  from mindsdb_sql_parser.ast import Insert, Identifier, CreateTable, TableColumn, DropTables
14
15
 
16
+ from mindsdb.api.executor.datahub.classes.response import DataHubResponse
15
17
  from mindsdb.api.executor.datahub.datanodes.datanode import DataNode
16
- from mindsdb.api.executor.data_types.response_type import RESPONSE_TYPE
17
18
  from mindsdb.api.executor.datahub.classes.tables_row import TablesRow
19
+ from mindsdb.api.executor.data_types.response_type import RESPONSE_TYPE
18
20
  from mindsdb.api.executor.sql_query.result_set import ResultSet
19
21
  from mindsdb.integrations.utilities.utils import get_class_name
20
22
  from mindsdb.metrics import metrics
21
23
  from mindsdb.utilities import log
22
24
  from mindsdb.utilities.profiler import profiler
25
+ from mindsdb.integrations.libs.response import HandlerResponse
23
26
 
24
27
  logger = log.getLogger(__name__)
25
28
 
@@ -52,9 +55,6 @@ class IntegrationDataNode(DataNode):
52
55
  else:
53
56
  raise Exception(f"Can't get tables: {response.error_message}")
54
57
 
55
- def has_table(self, tableName):
56
- return True
57
-
58
58
  def get_table_columns(self, table_name: str, schema_name: Optional[str] = None):
59
59
  if 'schema_name' in inspect.signature(self.integration_handler.get_columns).parameters:
60
60
  response = self.integration_handler.get_columns(table_name, schema_name)
@@ -107,7 +107,7 @@ class IntegrationDataNode(DataNode):
107
107
  raise Exception(result.error_message)
108
108
 
109
109
  def create_table(self, table_name: Identifier, result_set: ResultSet = None, columns=None,
110
- is_replace=False, is_create=False):
110
+ is_replace=False, is_create=False) -> DataHubResponse:
111
111
  # is_create - create table
112
112
  # is_replace - drop table if exists
113
113
  # is_create==False and is_replace==False: just insert
@@ -164,14 +164,14 @@ class IntegrationDataNode(DataNode):
164
164
 
165
165
  if result_set is None:
166
166
  # it is just a 'create table'
167
- return
167
+ return DataHubResponse()
168
168
 
169
169
  # native insert
170
170
  if hasattr(self.integration_handler, 'insert'):
171
171
  df = result_set.to_df()
172
172
 
173
- self.integration_handler.insert(table_name.parts[-1], df)
174
- return
173
+ result: HandlerResponse = self.integration_handler.insert(table_name.parts[-1], df)
174
+ return DataHubResponse(affected_rows=result.affected_rows)
175
175
 
176
176
  insert_columns = [Identifier(parts=[x.alias]) for x in result_set.columns]
177
177
 
@@ -195,7 +195,7 @@ class IntegrationDataNode(DataNode):
195
195
 
196
196
  if len(values) == 0:
197
197
  # not need to insert
198
- return
198
+ return DataHubResponse()
199
199
 
200
200
  insert_ast = Insert(
201
201
  table=table_name,
@@ -213,7 +213,9 @@ class IntegrationDataNode(DataNode):
213
213
  if result.type == RESPONSE_TYPE.ERROR:
214
214
  raise Exception(result.error_message)
215
215
 
216
- def _query(self, query):
216
+ return DataHubResponse(affected_rows=result.affected_rows)
217
+
218
+ def _query(self, query) -> HandlerResponse:
217
219
  time_before_query = time.perf_counter()
218
220
  result = self.integration_handler.query(query)
219
221
  elapsed_seconds = time.perf_counter() - time_before_query
@@ -229,7 +231,7 @@ class IntegrationDataNode(DataNode):
229
231
  response_size_with_labels.observe(num_rows)
230
232
  return result
231
233
 
232
- def _native_query(self, native_query):
234
+ def _native_query(self, native_query) -> HandlerResponse:
233
235
  time_before_query = time.perf_counter()
234
236
  result = self.integration_handler.native_query(native_query)
235
237
  elapsed_seconds = time.perf_counter() - time_before_query
@@ -246,13 +248,13 @@ class IntegrationDataNode(DataNode):
246
248
  return result
247
249
 
248
250
  @profiler.profile()
249
- def query(self, query=None, native_query=None, session=None):
251
+ def query(self, query: Optional[ASTNode] = None, native_query: Optional[str] = None, session=None) -> DataHubResponse:
250
252
  try:
251
253
  if query is not None:
252
- result = self._query(query)
254
+ result: HandlerResponse = self._query(query)
253
255
  else:
254
256
  # try to fetch native query
255
- result = self._native_query(native_query)
257
+ result: HandlerResponse = self._native_query(native_query)
256
258
  except Exception as e:
257
259
  msg = str(e).strip()
258
260
  if msg == '':
@@ -263,7 +265,7 @@ class IntegrationDataNode(DataNode):
263
265
  if result.type == RESPONSE_TYPE.ERROR:
264
266
  raise Exception(f'Error in {self.integration_name}: {result.error_message}')
265
267
  if result.type == RESPONSE_TYPE.OK:
266
- return pd.DataFrame(), []
268
+ return DataHubResponse(affected_rows=result.affected_rows)
267
269
 
268
270
  df = result.data_frame
269
271
  # region clearing df from NaN values
@@ -286,4 +288,8 @@ class IntegrationDataNode(DataNode):
286
288
  for k, v in df.dtypes.items()
287
289
  ]
288
290
 
289
- return df, columns_info
291
+ return DataHubResponse(
292
+ data_frame=df,
293
+ columns=columns_info,
294
+ affected_rows=result.affected_rows
295
+ )
@@ -9,6 +9,7 @@ from mindsdb.interfaces.jobs.jobs_controller import JobsController
9
9
  from mindsdb.interfaces.skills.skills_controller import SkillsController
10
10
  from mindsdb.interfaces.database.views import ViewController
11
11
  from mindsdb.interfaces.database.projects import ProjectController
12
+ from mindsdb.interfaces.query_context.context_controller import query_context_controller
12
13
 
13
14
  from mindsdb.api.executor.datahub.datanodes.system_tables import Table
14
15
 
@@ -326,7 +327,8 @@ class ChatbotsTable(MdbTable):
326
327
 
327
328
  class KBTable(MdbTable):
328
329
  name = 'KNOWLEDGE_BASES'
329
- columns = ["NAME", "PROJECT", "MODEL", "STORAGE", "PARAMS"]
330
+ columns = ["NAME", "PROJECT", "MODEL", "STORAGE", "PARAMS",
331
+ "INSERT_STARTED_AT", "INSERT_FINISHED_AT", "PROCESSED_ROWS", "ERROR", "QUERY_ID"]
330
332
 
331
333
  @classmethod
332
334
  def get_data(cls, query: ASTNode = None, inf_schema=None, **kwargs):
@@ -336,17 +338,36 @@ class KBTable(MdbTable):
336
338
  controller = KnowledgeBaseController(inf_schema.session)
337
339
  kb_list = controller.list(project_name)
338
340
 
341
+ # shouldn't be a lot of queries, we can fetch them all
342
+ queries_data = {
343
+ item['id']: item
344
+ for item in query_context_controller.list_queries()
345
+ }
346
+
339
347
  data = []
340
348
 
341
349
  for kb in kb_list:
342
350
  vector_database_name = kb['vector_database'] or ''
343
351
 
352
+ query_item = {}
353
+ query_id = kb['query_id']
354
+ if query_id is not None:
355
+ if query_id in queries_data:
356
+ query_item = queries_data.get(query_id)
357
+ else:
358
+ query_id = None
359
+
344
360
  data.append((
345
361
  kb['name'],
346
362
  kb['project_name'],
347
363
  kb['embedding_model'],
348
364
  vector_database_name + '.' + kb['vector_database_table'],
349
365
  to_json(kb['params']),
366
+ query_item.get('started_at'),
367
+ query_item.get('finished_at'),
368
+ query_item.get('processed_rows'),
369
+ query_item.get('error'),
370
+ query_id,
350
371
  ))
351
372
 
352
373
  return pd.DataFrame(data, columns=cls.columns)
@@ -426,3 +447,24 @@ class ViewsTable(MdbTable):
426
447
  data = [[row[k] for k in columns_lower] for row in data]
427
448
 
428
449
  return pd.DataFrame(data, columns=cls.columns)
450
+
451
+
452
+ class QueriesTable(MdbTable):
453
+ name = 'QUERIES'
454
+ columns = ["ID", "STARTED_AT", "FINISHED_AT", "PROCESSED_ROWS", "ERROR", "SQL", "PARAMETERS", "CONTEXT", "UPDATED_AT"]
455
+
456
+ @classmethod
457
+ def get_data(cls, **kwargs):
458
+ """
459
+ Returns all queries in progres or recently completed
460
+ Only queries marked as is_resumable by planner are stored in this table
461
+ :param kwargs:
462
+ :return:
463
+ """
464
+
465
+ data = query_context_controller.list_queries()
466
+ columns_lower = [col.lower() for col in cls.columns]
467
+
468
+ data = [[row[k] for k in columns_lower] for row in data]
469
+
470
+ return pd.DataFrame(data, columns=cls.columns)