MindsDB 25.5.4.1__py3-none-any.whl → 25.6.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/api/a2a/agent.py +28 -25
- mindsdb/api/a2a/common/server/server.py +32 -26
- mindsdb/api/a2a/run_a2a.py +1 -1
- mindsdb/api/executor/command_executor.py +69 -14
- mindsdb/api/executor/datahub/datanodes/integration_datanode.py +49 -65
- mindsdb/api/executor/datahub/datanodes/project_datanode.py +29 -48
- mindsdb/api/executor/datahub/datanodes/system_tables.py +35 -61
- mindsdb/api/executor/planner/plan_join.py +67 -77
- mindsdb/api/executor/planner/query_planner.py +176 -155
- mindsdb/api/executor/planner/steps.py +37 -12
- mindsdb/api/executor/sql_query/result_set.py +45 -64
- mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +14 -18
- mindsdb/api/executor/sql_query/steps/fetch_dataframe_partition.py +17 -18
- mindsdb/api/executor/sql_query/steps/insert_step.py +13 -33
- mindsdb/api/executor/sql_query/steps/subselect_step.py +43 -35
- mindsdb/api/executor/utilities/sql.py +42 -48
- mindsdb/api/http/namespaces/config.py +1 -1
- mindsdb/api/http/namespaces/file.py +14 -23
- mindsdb/api/mysql/mysql_proxy/data_types/mysql_datum.py +12 -28
- mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/binary_resultset_row_package.py +59 -50
- mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/resultset_row_package.py +9 -8
- mindsdb/api/mysql/mysql_proxy/libs/constants/mysql.py +449 -461
- mindsdb/api/mysql/mysql_proxy/utilities/dump.py +87 -36
- mindsdb/integrations/handlers/file_handler/file_handler.py +15 -9
- mindsdb/integrations/handlers/file_handler/tests/test_file_handler.py +43 -24
- mindsdb/integrations/handlers/litellm_handler/litellm_handler.py +10 -3
- mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +26 -33
- mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +74 -51
- mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +305 -98
- mindsdb/integrations/handlers/salesforce_handler/salesforce_handler.py +53 -34
- mindsdb/integrations/handlers/salesforce_handler/salesforce_tables.py +136 -6
- mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +334 -83
- mindsdb/integrations/libs/api_handler.py +261 -57
- mindsdb/integrations/libs/base.py +100 -29
- mindsdb/integrations/utilities/files/file_reader.py +99 -73
- mindsdb/integrations/utilities/handler_utils.py +23 -8
- mindsdb/integrations/utilities/sql_utils.py +35 -40
- mindsdb/interfaces/agents/agents_controller.py +196 -192
- mindsdb/interfaces/agents/constants.py +7 -1
- mindsdb/interfaces/agents/langchain_agent.py +42 -11
- mindsdb/interfaces/agents/mcp_client_agent.py +29 -21
- mindsdb/interfaces/data_catalog/__init__.py +0 -0
- mindsdb/interfaces/data_catalog/base_data_catalog.py +54 -0
- mindsdb/interfaces/data_catalog/data_catalog_loader.py +359 -0
- mindsdb/interfaces/data_catalog/data_catalog_reader.py +34 -0
- mindsdb/interfaces/database/database.py +81 -57
- mindsdb/interfaces/database/integrations.py +220 -234
- mindsdb/interfaces/database/log.py +72 -104
- mindsdb/interfaces/database/projects.py +156 -193
- mindsdb/interfaces/file/file_controller.py +21 -65
- mindsdb/interfaces/knowledge_base/controller.py +63 -10
- mindsdb/interfaces/knowledge_base/evaluate.py +519 -0
- mindsdb/interfaces/knowledge_base/llm_client.py +75 -0
- mindsdb/interfaces/skills/custom/text2sql/mindsdb_kb_tools.py +83 -43
- mindsdb/interfaces/skills/skills_controller.py +54 -36
- mindsdb/interfaces/skills/sql_agent.py +109 -86
- mindsdb/interfaces/storage/db.py +223 -79
- mindsdb/migrations/versions/2025-05-28_a44643042fe8_added_data_catalog_tables.py +118 -0
- mindsdb/migrations/versions/2025-06-09_608e376c19a7_updated_data_catalog_data_types.py +58 -0
- mindsdb/utilities/config.py +9 -2
- mindsdb/utilities/log.py +35 -26
- mindsdb/utilities/ml_task_queue/task.py +19 -22
- mindsdb/utilities/render/sqlalchemy_render.py +129 -181
- mindsdb/utilities/starters.py +49 -1
- {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/METADATA +268 -268
- {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/RECORD +70 -62
- {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/WHEEL +0 -0
- {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
from typing import Any, Dict, Text
|
|
1
|
+
from typing import Any, Dict, List, Optional, Text
|
|
2
2
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
import salesforce_api
|
|
5
5
|
from salesforce_api.exceptions import AuthenticationError, RestRequestCouldNotBeUnderstoodError
|
|
6
6
|
|
|
7
|
-
from mindsdb.integrations.libs.api_handler import
|
|
7
|
+
from mindsdb.integrations.libs.api_handler import MetaAPIHandler
|
|
8
8
|
from mindsdb.integrations.libs.response import (
|
|
9
9
|
HandlerResponse as Response,
|
|
10
10
|
HandlerStatusResponse as StatusResponse,
|
|
11
|
-
RESPONSE_TYPE
|
|
11
|
+
RESPONSE_TYPE,
|
|
12
12
|
)
|
|
13
13
|
from mindsdb.integrations.handlers.salesforce_handler.salesforce_tables import create_table_class
|
|
14
14
|
from mindsdb.utilities import log
|
|
@@ -17,12 +17,12 @@ from mindsdb.utilities import log
|
|
|
17
17
|
logger = log.getLogger(__name__)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class SalesforceHandler(
|
|
20
|
+
class SalesforceHandler(MetaAPIHandler):
|
|
21
21
|
"""
|
|
22
22
|
This handler handles the connection and execution of SQL statements on Salesforce.
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
|
-
name =
|
|
25
|
+
name = "salesforce"
|
|
26
26
|
|
|
27
27
|
def __init__(self, name: Text, connection_data: Dict, **kwargs: Any) -> None:
|
|
28
28
|
"""
|
|
@@ -57,23 +57,23 @@ class SalesforceHandler(APIHandler):
|
|
|
57
57
|
return self.connection
|
|
58
58
|
|
|
59
59
|
# Mandatory connection parameters.
|
|
60
|
-
if not all(key in self.connection_data for key in [
|
|
60
|
+
if not all(key in self.connection_data for key in ["username", "password", "client_id", "client_secret"]):
|
|
61
61
|
raise ValueError("Required parameters (username, password, client_id, client_secret) must be provided.")
|
|
62
62
|
|
|
63
63
|
try:
|
|
64
64
|
self.connection = salesforce_api.Salesforce(
|
|
65
|
-
username=self.connection_data[
|
|
66
|
-
password=self.connection_data[
|
|
67
|
-
client_id=self.connection_data[
|
|
68
|
-
client_secret=self.connection_data[
|
|
69
|
-
is_sandbox=self.connection_data.get(
|
|
65
|
+
username=self.connection_data["username"],
|
|
66
|
+
password=self.connection_data["password"],
|
|
67
|
+
client_id=self.connection_data["client_id"],
|
|
68
|
+
client_secret=self.connection_data["client_secret"],
|
|
69
|
+
is_sandbox=self.connection_data.get("is_sandbox", False),
|
|
70
70
|
)
|
|
71
71
|
self.is_connected = True
|
|
72
72
|
|
|
73
73
|
# Register Salesforce tables.
|
|
74
74
|
for resource_name in self._get_resource_names():
|
|
75
75
|
table_class = create_table_class(resource_name)
|
|
76
|
-
self._register_table(resource_name
|
|
76
|
+
self._register_table(resource_name, table_class(self))
|
|
77
77
|
|
|
78
78
|
return self.connection
|
|
79
79
|
except AuthenticationError as auth_error:
|
|
@@ -96,10 +96,10 @@ class SalesforceHandler(APIHandler):
|
|
|
96
96
|
self.connect()
|
|
97
97
|
response.success = True
|
|
98
98
|
except (AuthenticationError, ValueError) as known_error:
|
|
99
|
-
logger.error(f
|
|
99
|
+
logger.error(f"Connection check to Salesforce failed, {known_error}!")
|
|
100
100
|
response.error_message = str(known_error)
|
|
101
101
|
except Exception as unknown_error:
|
|
102
|
-
logger.error(f
|
|
102
|
+
logger.error(f"Connection check to Salesforce failed due to an unknown error, {unknown_error}!")
|
|
103
103
|
response.error_message = str(unknown_error)
|
|
104
104
|
|
|
105
105
|
self.is_connected = response.success
|
|
@@ -123,7 +123,7 @@ class SalesforceHandler(APIHandler):
|
|
|
123
123
|
|
|
124
124
|
parsed_results = []
|
|
125
125
|
for result in results:
|
|
126
|
-
del result[
|
|
126
|
+
del result["attributes"]
|
|
127
127
|
|
|
128
128
|
# Check if the result contains any of the other Salesforce resources.
|
|
129
129
|
if any(key in self.resource_names for key in result.keys()):
|
|
@@ -131,8 +131,10 @@ class SalesforceHandler(APIHandler):
|
|
|
131
131
|
parsed_result = {}
|
|
132
132
|
for key, value in result.items():
|
|
133
133
|
if key in self.resource_names:
|
|
134
|
-
del value[
|
|
135
|
-
parsed_result.update(
|
|
134
|
+
del value["attributes"]
|
|
135
|
+
parsed_result.update(
|
|
136
|
+
{f"{key}_{sub_key}": sub_value for sub_key, sub_value in value.items()}
|
|
137
|
+
)
|
|
136
138
|
|
|
137
139
|
else:
|
|
138
140
|
parsed_result[key] = value
|
|
@@ -142,24 +144,13 @@ class SalesforceHandler(APIHandler):
|
|
|
142
144
|
else:
|
|
143
145
|
parsed_results.append(result)
|
|
144
146
|
|
|
145
|
-
response = Response(
|
|
146
|
-
RESPONSE_TYPE.TABLE,
|
|
147
|
-
pd.DataFrame(parsed_results)
|
|
148
|
-
)
|
|
147
|
+
response = Response(RESPONSE_TYPE.TABLE, pd.DataFrame(parsed_results))
|
|
149
148
|
except RestRequestCouldNotBeUnderstoodError as rest_error:
|
|
150
|
-
logger.error(f
|
|
151
|
-
response = Response(
|
|
152
|
-
RESPONSE_TYPE.ERROR,
|
|
153
|
-
error_code=0,
|
|
154
|
-
error_message=str(rest_error)
|
|
155
|
-
)
|
|
149
|
+
logger.error(f"Error running query: {query} on Salesforce, {rest_error}!")
|
|
150
|
+
response = Response(RESPONSE_TYPE.ERROR, error_code=0, error_message=str(rest_error))
|
|
156
151
|
except Exception as unknown_error:
|
|
157
|
-
logger.error(f
|
|
158
|
-
response = Response(
|
|
159
|
-
RESPONSE_TYPE.ERROR,
|
|
160
|
-
error_code=0,
|
|
161
|
-
error_message=str(unknown_error)
|
|
162
|
-
)
|
|
152
|
+
logger.error(f"Error running query: {query} on Salesforce, {unknown_error}!")
|
|
153
|
+
response = Response(RESPONSE_TYPE.ERROR, error_code=0, error_message=str(unknown_error))
|
|
163
154
|
|
|
164
155
|
return response
|
|
165
156
|
|
|
@@ -171,6 +162,34 @@ class SalesforceHandler(APIHandler):
|
|
|
171
162
|
None
|
|
172
163
|
"""
|
|
173
164
|
if not self.resource_names:
|
|
174
|
-
|
|
165
|
+
# Fetch the queryable list of Salesforce resources (sobjects).
|
|
166
|
+
self.resource_names = [
|
|
167
|
+
resource["name"]
|
|
168
|
+
for resource in self.connection.sobjects.describe()["sobjects"]
|
|
169
|
+
if resource.get("queryable", False)
|
|
170
|
+
]
|
|
175
171
|
|
|
176
172
|
return self.resource_names
|
|
173
|
+
|
|
174
|
+
def meta_get_tables(self, table_names: Optional[List[str]] = None) -> Response:
|
|
175
|
+
"""
|
|
176
|
+
Retrieves metadata for the specified tables (or all tables if no list is provided).
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
table_names (List): A list of table names for which to retrieve metadata.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Response: A response object containing the table metadata.
|
|
183
|
+
"""
|
|
184
|
+
connection = self.connect()
|
|
185
|
+
|
|
186
|
+
# Retrieve the metadata for all Salesforce resources.
|
|
187
|
+
main_metadata = connection.sobjects.describe()
|
|
188
|
+
|
|
189
|
+
if table_names:
|
|
190
|
+
# Filter the metadata for the specified tables.
|
|
191
|
+
main_metadata = [resource for resource in main_metadata["sobjects"] if resource["name"] in table_names]
|
|
192
|
+
else:
|
|
193
|
+
main_metadata = main_metadata["sobjects"]
|
|
194
|
+
|
|
195
|
+
return super().meta_get_tables(table_names=table_names, main_metadata=main_metadata)
|
|
@@ -2,8 +2,9 @@ from typing import Dict, List, Text
|
|
|
2
2
|
|
|
3
3
|
from mindsdb_sql_parser.ast import Select, Star, Identifier
|
|
4
4
|
import pandas as pd
|
|
5
|
+
from salesforce_api.exceptions import RestRequestCouldNotBeUnderstoodError
|
|
5
6
|
|
|
6
|
-
from mindsdb.integrations.libs.api_handler import
|
|
7
|
+
from mindsdb.integrations.libs.api_handler import MetaAPIResource
|
|
7
8
|
from mindsdb.integrations.utilities.sql_utils import FilterCondition, FilterOperator
|
|
8
9
|
from mindsdb.utilities import log
|
|
9
10
|
|
|
@@ -11,16 +12,28 @@ from mindsdb.utilities import log
|
|
|
11
12
|
logger = log.getLogger(__name__)
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
def create_table_class(resource_name: Text) ->
|
|
15
|
+
def create_table_class(resource_name: Text) -> MetaAPIResource:
|
|
15
16
|
"""
|
|
16
17
|
Creates a table class for the given Salesforce resource.
|
|
17
18
|
"""
|
|
18
19
|
|
|
19
|
-
class AnyTable(
|
|
20
|
+
class AnyTable(MetaAPIResource):
|
|
20
21
|
"""
|
|
21
22
|
This is the table abstraction for any resource of the Salesforce API.
|
|
22
23
|
"""
|
|
23
24
|
|
|
25
|
+
def __init__(self, *args, table_name=None, **kwargs):
|
|
26
|
+
"""
|
|
27
|
+
Initializes the AnyTable class.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
*args: Variable length argument list.
|
|
31
|
+
table_name (str): The name of the table that represents the Salesforce resource.
|
|
32
|
+
**kwargs: Arbitrary keyword arguments.
|
|
33
|
+
"""
|
|
34
|
+
super().__init__(*args, table_name=table_name, **kwargs)
|
|
35
|
+
self.resource_metadata = None
|
|
36
|
+
|
|
24
37
|
def select(self, query: Select) -> pd.DataFrame:
|
|
25
38
|
"""
|
|
26
39
|
Executes a SELECT SQL query represented by an ASTNode object on the Salesforce resource and retrieves the data (if any).
|
|
@@ -53,7 +66,7 @@ def create_table_class(resource_name: Text) -> APIResource:
|
|
|
53
66
|
results = client.sobjects.query(query_str)
|
|
54
67
|
|
|
55
68
|
for result in results:
|
|
56
|
-
del result[
|
|
69
|
+
del result["attributes"]
|
|
57
70
|
|
|
58
71
|
df = pd.DataFrame(results)
|
|
59
72
|
df.rename(columns=column_aliases, inplace=True)
|
|
@@ -107,7 +120,7 @@ def create_table_class(resource_name: Text) -> APIResource:
|
|
|
107
120
|
conditions (List[FilterCondition]): The conditions to be validated.
|
|
108
121
|
"""
|
|
109
122
|
# Salesforce API does not support filtering items based on attributes other than 'Id'. Raise an error if any other column is used.
|
|
110
|
-
if len(conditions) != 1 or conditions[0].column !=
|
|
123
|
+
if len(conditions) != 1 or conditions[0].column != "Id":
|
|
111
124
|
raise ValueError("Only the 'Id' column can be used to filter items.")
|
|
112
125
|
|
|
113
126
|
# Only the 'equals' and 'in' operators can be used on the 'Id' column for deletion. Raise an error if any other operator is used.
|
|
@@ -116,6 +129,19 @@ def create_table_class(resource_name: Text) -> APIResource:
|
|
|
116
129
|
|
|
117
130
|
return conditions[0].value if isinstance(conditions[0].value, list) else [conditions[0].value]
|
|
118
131
|
|
|
132
|
+
def _get_resource_metadata(self) -> Dict:
|
|
133
|
+
"""
|
|
134
|
+
Retrieves metadata about the Salesforce resource.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Dict: A dictionary containing metadata about the Salesforce resource.
|
|
138
|
+
"""
|
|
139
|
+
if self.resource_metadata:
|
|
140
|
+
return self.resource_metadata
|
|
141
|
+
|
|
142
|
+
client = self.handler.connect()
|
|
143
|
+
return getattr(client.sobjects, resource_name).describe()
|
|
144
|
+
|
|
119
145
|
def get_columns(self) -> List[Text]:
|
|
120
146
|
"""
|
|
121
147
|
Retrieves the attributes (columns) of the Salesforce resource.
|
|
@@ -123,7 +149,111 @@ def create_table_class(resource_name: Text) -> APIResource:
|
|
|
123
149
|
Returns:
|
|
124
150
|
List[Text]: A list of Attributes (columns) of the Salesforce resource.
|
|
125
151
|
"""
|
|
152
|
+
return [field["name"] for field in self._get_resource_metadata()["fields"]]
|
|
153
|
+
|
|
154
|
+
def meta_get_tables(self, table_name: str, main_metadata: Dict) -> Dict:
|
|
155
|
+
"""
|
|
156
|
+
Retrieves table metadata for the Salesforce resource.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
table_name (str): The name given to the table that represents the Salesforce resource.
|
|
160
|
+
main_metadata (Dict): The main metadata dictionary containing information about all Salesforce resources.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Dict: A dictionary containing table metadata for the Salesforce resource.
|
|
164
|
+
"""
|
|
126
165
|
client = self.handler.connect()
|
|
127
|
-
|
|
166
|
+
|
|
167
|
+
resource_metadata = next(
|
|
168
|
+
(resource for resource in main_metadata if resource["name"] == resource_name),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# Get row count if Id column is aggregatable.
|
|
172
|
+
row_count = None
|
|
173
|
+
# if next(field for field in resource_metadata['fields'] if field['name'] == 'Id').get('aggregatable', False):
|
|
174
|
+
try:
|
|
175
|
+
row_count = client.sobjects.query(f"SELECT COUNT(Id) FROM {resource_name}")[0]["expr0"]
|
|
176
|
+
except RestRequestCouldNotBeUnderstoodError as request_error:
|
|
177
|
+
logger.warning(f"Failed to get row count for {resource_name}: {request_error}")
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
"table_name": table_name,
|
|
181
|
+
"table_type": "BASE TABLE",
|
|
182
|
+
"table_description": resource_metadata.get("label", ""),
|
|
183
|
+
"row_count": row_count,
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
def meta_get_columns(self, table_name: str) -> List[Dict]:
|
|
187
|
+
"""
|
|
188
|
+
Retrieves column metadata for the Salesforce resource.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
table_name (str): The name given to the table that represents the Salesforce resource.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
List[Dict]: A list of dictionaries containing column metadata for the Salesforce resource.
|
|
195
|
+
"""
|
|
196
|
+
resource_metadata = self._get_resource_metadata()
|
|
197
|
+
|
|
198
|
+
column_metadata = []
|
|
199
|
+
for field in resource_metadata["fields"]:
|
|
200
|
+
column_metadata.append(
|
|
201
|
+
{
|
|
202
|
+
"table_name": table_name,
|
|
203
|
+
"column_name": field["name"],
|
|
204
|
+
"data_type": field["type"],
|
|
205
|
+
"is_nullable": field.get("nillable", False),
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
return column_metadata
|
|
210
|
+
|
|
211
|
+
def meta_get_primary_keys(self, table_name: str) -> List[Dict]:
|
|
212
|
+
"""
|
|
213
|
+
Retrieves the primary keys for the Salesforce resource.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
table_name (str): The name given to the table that represents the Salesforce resource.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
List[Dict]: A list of dictionaries containing primary key metadata for the Salesforce resource.
|
|
220
|
+
"""
|
|
221
|
+
return [
|
|
222
|
+
{
|
|
223
|
+
"table_name": table_name,
|
|
224
|
+
"column_name": "Id",
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
def meta_get_foreign_keys(self, table_name: str, all_tables: List[str]) -> List[Dict]:
|
|
229
|
+
"""
|
|
230
|
+
Retrieves the foreign keys for the Salesforce resource.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
table_name (str): The name given to the table that represents the Salesforce resource.
|
|
234
|
+
all_tables (List[str]): A list of all table names in the Salesforce database.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
List[Dict]: A list of dictionaries containing foreign key metadata for the Salesforce resource.
|
|
238
|
+
"""
|
|
239
|
+
resource_metadata = self._get_resource_metadata()
|
|
240
|
+
|
|
241
|
+
foreign_key_metadata = []
|
|
242
|
+
for child_relationship in resource_metadata.get("childRelationships", []):
|
|
243
|
+
# Skip if the child relationship is not one of the supported tables.
|
|
244
|
+
child_table_name = child_relationship["childSObject"]
|
|
245
|
+
if child_table_name not in all_tables:
|
|
246
|
+
continue
|
|
247
|
+
|
|
248
|
+
foreign_key_metadata.append(
|
|
249
|
+
{
|
|
250
|
+
"parent_table_name": table_name,
|
|
251
|
+
"parent_column_name": "Id",
|
|
252
|
+
"child_table_name": child_table_name,
|
|
253
|
+
"child_column_name": child_relationship["field"],
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
return foreign_key_metadata
|
|
128
258
|
|
|
129
259
|
return AnyTable
|