From 2e4aed8b5a9187240d73cad55e5890a778b687d1 Mon Sep 17 00:00:00 2001 From: Anish Umale Date: Wed, 31 Jul 2024 04:12:12 +0530 Subject: [PATCH 1/6] add target_table_info in list_joinable's response --- db/sql/10_msar_joinable_tables.sql | 30 +++- db/tables/operations/select.py | 4 +- docs/docs/api/rpc.md | 1 + mathesar/rpc/tables/base.py | 29 +++- mathesar/tests/rpc/tables/test_t_base.py | 188 +++++++++++++---------- 5 files changed, 158 insertions(+), 94 deletions(-) diff --git a/db/sql/10_msar_joinable_tables.sql b/db/sql/10_msar_joinable_tables.sql index eab50b25ec..a397d661c7 100644 --- a/db/sql/10_msar_joinable_tables.sql +++ b/db/sql/10_msar_joinable_tables.sql @@ -31,8 +31,8 @@ whether to travel from referrer to referant (when False) or from referant to ref DROP TYPE IF EXISTS msar.joinable_tables CASCADE; CREATE TYPE msar.joinable_tables AS ( - base integer, -- The OID of the table from which the paths start - target integer, -- The OID of the table where the paths end + base bigint, -- The OID of the table from which the paths start + target bigint, -- The OID of the table where the paths end join_path jsonb, -- A JSONB array of arrays of arrays fkey_path jsonb, depth integer, @@ -131,8 +131,28 @@ SELECT * FROM output_cte; $$ LANGUAGE sql; +DROP FUNCTION IF EXISTS msar.get_joinable_tables(integer, oid); CREATE OR REPLACE FUNCTION msar.get_joinable_tables(max_depth integer, table_id oid) RETURNS - SETOF msar.joinable_tables AS $$ - SELECT * FROM msar.get_joinable_tables(max_depth) WHERE base=table_id -$$ LANGUAGE sql; +jsonb AS $$ + WITH jt_cte AS ( + SELECT * FROM msar.get_joinable_tables(max_depth) WHERE base=table_id + ), target_cte AS ( + SELECT jsonb_build_object( + pga.attrelid::text, msar.get_relation_name(pga.attrelid), + 'columns', jsonb_agg( + jsonb_build_object( + pga.attnum::text, jsonb_build_object( + 'name', pga.attname, 'type', CASE WHEN attndims>0 THEN '_array' ELSE atttypid::regtype::text END + ) + ) + ) + ) as tt + FROM pg_catalog.pg_attribute AS pga, jt_cte + WHERE pga.attrelid=jt_cte.target AND pga.attnum > 0 and NOT pga.attisdropped GROUP BY pga.attrelid + ) + SELECT jsonb_build_object( + 'joinable_tables', jsonb_agg(to_jsonb(jt_cte.*)), + 'target_table_info', jsonb_agg(target_cte.tt) + ) FROM target_cte, jt_cte +$$ LANGUAGE sql RETURNS NULL ON NULL INPUT; diff --git a/db/tables/operations/select.py b/db/tables/operations/select.py index e0d4b7365e..a25fca1d43 100644 --- a/db/tables/operations/select.py +++ b/db/tables/operations/select.py @@ -3,7 +3,7 @@ ) from sqlalchemy.dialects.postgresql import JSONB -from db.connection import exec_msar_func, select_from_msar_func +from db.connection import exec_msar_func from db.utils import execute_statement, get_pg_catalog_table BASE = 'base' @@ -60,7 +60,7 @@ def get_table_info(schema, conn): def list_joinable_tables(table_oid, conn, max_depth): - return select_from_msar_func(conn, 'get_joinable_tables', max_depth, table_oid) + return exec_msar_func(conn, 'get_joinable_tables', max_depth, table_oid).fetchone()[0] def reflect_table(name, schema, engine, metadata, connection_to_use=None, keep_existing=False): diff --git a/docs/docs/api/rpc.md b/docs/docs/api/rpc.md index 0f969220fb..d88f8489b9 100644 --- a/docs/docs/api/rpc.md +++ b/docs/docs/api/rpc.md @@ -113,6 +113,7 @@ To use an RPC function: - list_joinable - TableInfo - SettableTableInfo + - JoinableTableRecord - JoinableTableInfo ## Table Metadata diff --git a/mathesar/rpc/tables/base.py b/mathesar/rpc/tables/base.py index 8b076a94e5..9ee9b4e73e 100644 --- a/mathesar/rpc/tables/base.py +++ b/mathesar/rpc/tables/base.py @@ -56,9 +56,9 @@ class SettableTableInfo(TypedDict): columns: Optional[list[SettableColumnInfo]] -class JoinableTableInfo(TypedDict): +class JoinableTableRecord(TypedDict): """ - Information about a joinable table. + Information about a singular joinable table. Attributes: base: The OID of the table from which the paths start @@ -102,6 +102,25 @@ def from_dict(cls, joinables): ) +class JoinableTableInfo(TypedDict): + """ + Information about joinable table(s). + + Attributes: + joinable_tables: List of reachable joinable table(s) from a base table. + target_table_info: Additional info about target table(s) and its column(s). + """ + joinable_tables: list[JoinableTableRecord] + target_table_info: list + + @classmethod + def from_dict(cls, joinable_dict): + return cls( + joinable_tables=[JoinableTableRecord.from_dict(j) for j in joinable_dict["joinable_tables"]], + target_table_info=joinable_dict["target_table_info"] + ) + + @rpc_method(name="tables.list") @http_basic_auth_login_required @handle_rpc_exceptions @@ -290,7 +309,7 @@ def list_joinable( database_id: int, max_depth: int = 3, **kwargs -) -> list[JoinableTableInfo]: +) -> JoinableTableInfo: """ List details for joinable tables. @@ -304,8 +323,8 @@ def list_joinable( """ user = kwargs.get(REQUEST_KEY).user with connect(database_id, user) as conn: - joinables = list_joinable_tables(table_oid, conn, max_depth) - return [JoinableTableInfo.from_dict(joinable) for joinable in joinables] + joinable_dict = list_joinable_tables(table_oid, conn, max_depth) + return JoinableTableInfo.from_dict(joinable_dict) @rpc_method(name="tables.list_with_metadata") diff --git a/mathesar/tests/rpc/tables/test_t_base.py b/mathesar/tests/rpc/tables/test_t_base.py index 3190502f9b..082315b783 100644 --- a/mathesar/tests/rpc/tables/test_t_base.py +++ b/mathesar/tests/rpc/tables/test_t_base.py @@ -288,107 +288,131 @@ def mock_connect(_database_id, user): def mock_list_joinable_tables(_table_oid, conn, max_depth): if _table_oid != table_oid: raise AssertionError('incorrect parameters passed') - return [ + return { + 'joinable_tables': [ + { + 'base': 2254329, + 'depth': 1, + 'target': 2254334, + 'fkey_path': [[2254406, False]], + 'join_path': [[[2254329, 2], [2254334, 1]]], + 'multiple_results': False + }, + { + 'base': 2254329, + 'depth': 1, + 'target': 2254334, + 'fkey_path': [[2254406, False]], + 'join_path': [[[2254329, 2], [2254334, 1]]], + 'multiple_results': False + }, + { + 'base': 2254329, + 'depth': 1, + 'target': 2254350, + 'fkey_path': [[2254411, False]], + 'join_path': [[[2254329, 3], [2254350, 1]]], + 'multiple_results': False + }, + { + 'base': 2254329, + 'depth': 1, + 'target': 2254350, + 'fkey_path': [[2254411, False]], + 'join_path': [[[2254329, 3], [2254350, 1]]], + 'multiple_results': False + }], + 'target_table_info': [ + { + '2254334': 'Items', + 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, + {'2': {'name': 'Barcode', 'type': 'text'}}, + {'3': {'name': 'Acquisition Date', 'type': 'date'}}, + {'5': {'name': 'Book', 'type': 'integer'}}] + }, + { + '2254350': 'Patrons', + 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, + {'2': {'name': 'First Name', 'type': 'text'}}, + {'3': {'name': 'Last Name', 'type': 'text'}}] + }, + { + '2254334': 'Items', + 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, + {'2': {'name': 'Barcode', 'type': 'text'}}, + {'3': {'name': 'Acquisition Date', 'type': 'date'}}, + {'5': {'name': 'Book', 'type': 'integer'}}] + }, + { + '2254350': 'Patrons', + 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, + {'2': {'name': 'First Name', 'type': 'text'}}, + {'3': {'name': 'Last Name', 'type': 'text'}}] + }] + } + expected_dict = { + 'joinable_tables': [ { 'base': 2254329, + 'depth': 1, 'target': 2254334, - 'join_path': [[[2254329, 2], [2254334, 1]]], 'fkey_path': [[2254406, False]], - 'depth': 1, + 'join_path': [[[2254329, 2], [2254334, 1]]], 'multiple_results': False }, { 'base': 2254329, - 'target': 2254350, - 'join_path': [[[2254329, 3], [2254350, 1]]], - 'fkey_path': [[2254411, False]], 'depth': 1, + 'target': 2254334, + 'fkey_path': [[2254406, False]], + 'join_path': [[[2254329, 2], [2254334, 1]]], 'multiple_results': False }, { 'base': 2254329, - 'target': 2254321, - 'join_path': [[[2254329, 2], [2254334, 1]], [[2254334, 5], [2254321, 1]]], - 'fkey_path': [[2254406, False], [2254399, False]], - 'depth': 2, + 'depth': 1, + 'target': 2254350, + 'fkey_path': [[2254411, False]], + 'join_path': [[[2254329, 3], [2254350, 1]]], 'multiple_results': False }, { 'base': 2254329, - 'target': 2254358, - 'join_path': [ - [[2254329, 2], [2254334, 1]], - [[2254334, 5], [2254321, 1]], - [[2254321, 11], [2254358, 1]] - ], - 'fkey_path': [[2254406, False], [2254399, False], [2254394, False]], - 'depth': 3, + 'depth': 1, + 'target': 2254350, + 'fkey_path': [[2254411, False]], + 'join_path': [[[2254329, 3], [2254350, 1]]], 'multiple_results': False + }], + 'target_table_info': [ + { + '2254334': 'Items', + 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, + {'2': {'name': 'Barcode', 'type': 'text'}}, + {'3': {'name': 'Acquisition Date', 'type': 'date'}}, + {'5': {'name': 'Book', 'type': 'integer'}}] }, { - 'base': 2254329, - 'target': 2254313, - 'join_path': [ - [[2254329, 2], [2254334, 1]], - [[2254334, 5], [2254321, 1]], - [[2254321, 10], [2254313, 1]] - ], - 'fkey_path': [[2254406, False], [2254399, False], [2254389, False]], - 'depth': 3, - 'multiple_results': False - } - ] - expected_list = [ - { - 'base': 2254329, - 'target': 2254334, - 'join_path': [[[2254329, 2], [2254334, 1]]], - 'fkey_path': [[2254406, False]], - 'depth': 1, - 'multiple_results': False - }, - { - 'base': 2254329, - 'target': 2254350, - 'join_path': [[[2254329, 3], [2254350, 1]]], - 'fkey_path': [[2254411, False]], - 'depth': 1, - 'multiple_results': False - }, - { - 'base': 2254329, - 'target': 2254321, - 'join_path': [[[2254329, 2], [2254334, 1]], [[2254334, 5], [2254321, 1]]], - 'fkey_path': [[2254406, False], [2254399, False]], - 'depth': 2, - 'multiple_results': False - }, - { - 'base': 2254329, - 'target': 2254358, - 'join_path': [ - [[2254329, 2], [2254334, 1]], - [[2254334, 5], [2254321, 1]], - [[2254321, 11], [2254358, 1]] - ], - 'fkey_path': [[2254406, False], [2254399, False], [2254394, False]], - 'depth': 3, - 'multiple_results': False - }, - { - 'base': 2254329, - 'target': 2254313, - 'join_path': [ - [[2254329, 2], [2254334, 1]], - [[2254334, 5], [2254321, 1]], - [[2254321, 10], [2254313, 1]] - ], - 'fkey_path': [[2254406, False], [2254399, False], [2254389, False]], - 'depth': 3, - 'multiple_results': False - } - ] + '2254350': 'Patrons', + 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, + {'2': {'name': 'First Name', 'type': 'text'}}, + {'3': {'name': 'Last Name', 'type': 'text'}}] + }, + { + '2254334': 'Items', + 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, + {'2': {'name': 'Barcode', 'type': 'text'}}, + {'3': {'name': 'Acquisition Date', 'type': 'date'}}, + {'5': {'name': 'Book', 'type': 'integer'}}] + }, + { + '2254350': 'Patrons', + 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, + {'2': {'name': 'First Name', 'type': 'text'}}, + {'3': {'name': 'Last Name', 'type': 'text'}}] + }] + } monkeypatch.setattr(tables.base, 'connect', mock_connect) monkeypatch.setattr(tables.base, 'list_joinable_tables', mock_list_joinable_tables) - actual_list = tables.list_joinable(table_oid=2254329, database_id=11, max_depth=3, request=request) - assert expected_list == actual_list + actual_dict = tables.list_joinable(table_oid=2254329, database_id=11, max_depth=1, request=request) + assert expected_dict == actual_dict From f2c62d92cedc0470fde0d57d562ce9273d7722fe Mon Sep 17 00:00:00 2001 From: Anish Umale Date: Wed, 31 Jul 2024 17:53:54 +0530 Subject: [PATCH 2/6] fix aggregation for target_table_info --- db/sql/10_msar_joinable_tables.sql | 46 ++++++++++++++++++------------ 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/db/sql/10_msar_joinable_tables.sql b/db/sql/10_msar_joinable_tables.sql index a397d661c7..ebde573cd0 100644 --- a/db/sql/10_msar_joinable_tables.sql +++ b/db/sql/10_msar_joinable_tables.sql @@ -135,24 +135,32 @@ DROP FUNCTION IF EXISTS msar.get_joinable_tables(integer, oid); CREATE OR REPLACE FUNCTION msar.get_joinable_tables(max_depth integer, table_id oid) RETURNS jsonb AS $$ - WITH jt_cte AS ( - SELECT * FROM msar.get_joinable_tables(max_depth) WHERE base=table_id - ), target_cte AS ( - SELECT jsonb_build_object( - pga.attrelid::text, msar.get_relation_name(pga.attrelid), - 'columns', jsonb_agg( - jsonb_build_object( - pga.attnum::text, jsonb_build_object( - 'name', pga.attname, 'type', CASE WHEN attndims>0 THEN '_array' ELSE atttypid::regtype::text END +DECLARE + joinable_tables jsonb; + target_table_info jsonb; +BEGIN + CREATE TEMP TABLE jt_cte AS + SELECT * FROM msar.get_joinable_tables(max_depth) WHERE base=table_id; + SELECT jsonb_agg(to_jsonb(jt_cte.*)) INTO joinable_tables FROM jt_cte; + WITH target_cte AS ( + SELECT pga.attrelid::text AS tt_oid, + jsonb_build_object( + 'name', msar.get_relation_name(pga.attrelid), + 'columns', jsonb_object_agg( + pga.attnum::text, jsonb_build_object( + 'name', pga.attname, 'type', CASE WHEN attndims>0 THEN '_array' ELSE atttypid::regtype::text END + ) ) - ) - ) - ) as tt - FROM pg_catalog.pg_attribute AS pga, jt_cte - WHERE pga.attrelid=jt_cte.target AND pga.attnum > 0 and NOT pga.attisdropped GROUP BY pga.attrelid + ) AS tt_info + FROM pg_catalog.pg_attribute AS pga + LEFT JOIN jt_cte ON pga.attrelid = jt_cte.target + WHERE pga.attrelid=jt_cte.target AND pga.attnum > 0 and NOT pga.attisdropped + GROUP BY pga.attrelid ) - SELECT jsonb_build_object( - 'joinable_tables', jsonb_agg(to_jsonb(jt_cte.*)), - 'target_table_info', jsonb_agg(target_cte.tt) - ) FROM target_cte, jt_cte -$$ LANGUAGE sql RETURNS NULL ON NULL INPUT; + SELECT jsonb_object_agg(tt_oid, tt_info) INTO target_table_info FROM target_cte; + RETURN jsonb_build_object( + 'joinable_tables', joinable_tables, + 'target_table_info', target_table_info + ); +END; +$$ LANGUAGE plpgsql RETURNS NULL ON NULL INPUT; From 67161ffdd99ad080577b09d0c51e7214a088bee4 Mon Sep 17 00:00:00 2001 From: Anish Umale Date: Wed, 31 Jul 2024 18:01:08 +0530 Subject: [PATCH 3/6] nest attnum --- db/sql/10_msar_joinable_tables.sql | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/db/sql/10_msar_joinable_tables.sql b/db/sql/10_msar_joinable_tables.sql index ebde573cd0..05d4002ee9 100644 --- a/db/sql/10_msar_joinable_tables.sql +++ b/db/sql/10_msar_joinable_tables.sql @@ -143,12 +143,14 @@ BEGIN SELECT * FROM msar.get_joinable_tables(max_depth) WHERE base=table_id; SELECT jsonb_agg(to_jsonb(jt_cte.*)) INTO joinable_tables FROM jt_cte; WITH target_cte AS ( - SELECT pga.attrelid::text AS tt_oid, + SELECT pga.attrelid AS tt_oid, jsonb_build_object( 'name', msar.get_relation_name(pga.attrelid), - 'columns', jsonb_object_agg( - pga.attnum::text, jsonb_build_object( - 'name', pga.attname, 'type', CASE WHEN attndims>0 THEN '_array' ELSE atttypid::regtype::text END + 'columns', jsonb_agg( + jsonb_build_object( + 'attnum', pga.attnum, + 'name', pga.attname, + 'type', CASE WHEN attndims>0 THEN '_array' ELSE atttypid::regtype::text END ) ) ) AS tt_info From 5e1172a30e7e456555e6839cfc2ba2201d077c77 Mon Sep 17 00:00:00 2001 From: Anish Umale Date: Thu, 1 Aug 2024 02:30:20 +0530 Subject: [PATCH 4/6] revert to attnum as keys instead of nested attumn --- db/sql/10_msar_joinable_tables.sql | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/db/sql/10_msar_joinable_tables.sql b/db/sql/10_msar_joinable_tables.sql index 05d4002ee9..6fbe6e3d24 100644 --- a/db/sql/10_msar_joinable_tables.sql +++ b/db/sql/10_msar_joinable_tables.sql @@ -146,9 +146,8 @@ BEGIN SELECT pga.attrelid AS tt_oid, jsonb_build_object( 'name', msar.get_relation_name(pga.attrelid), - 'columns', jsonb_agg( - jsonb_build_object( - 'attnum', pga.attnum, + 'columns', jsonb_object_agg( + pga.attnum, jsonb_build_object( 'name', pga.attname, 'type', CASE WHEN attndims>0 THEN '_array' ELSE atttypid::regtype::text END ) From 9a86a475854c85408cd82e1b77c68d7a08efdf16 Mon Sep 17 00:00:00 2001 From: Anish Umale Date: Thu, 1 Aug 2024 15:48:42 +0530 Subject: [PATCH 5/6] change to pure cte based implementation and make joinable table functions stable --- db/sql/10_msar_joinable_tables.sql | 33 ++++++++++++++---------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/db/sql/10_msar_joinable_tables.sql b/db/sql/10_msar_joinable_tables.sql index 6fbe6e3d24..14902fbf05 100644 --- a/db/sql/10_msar_joinable_tables.sql +++ b/db/sql/10_msar_joinable_tables.sql @@ -40,6 +40,7 @@ CREATE TYPE msar.joinable_tables AS ( ); +DROP FUNCTION IF EXISTS msar.get_joinable_tables(integer); CREATE OR REPLACE FUNCTION msar.get_joinable_tables(max_depth integer) RETURNS SETOF msar.joinable_tables AS $$/* This function returns a table of msar.joinable_tables objects, giving paths to various @@ -128,21 +129,16 @@ UNION ALL FROM search_fkey_graph ) SELECT * FROM output_cte; -$$ LANGUAGE sql; +$$ LANGUAGE SQL STABLE; DROP FUNCTION IF EXISTS msar.get_joinable_tables(integer, oid); CREATE OR REPLACE FUNCTION msar.get_joinable_tables(max_depth integer, table_id oid) RETURNS jsonb AS $$ -DECLARE - joinable_tables jsonb; - target_table_info jsonb; -BEGIN - CREATE TEMP TABLE jt_cte AS - SELECT * FROM msar.get_joinable_tables(max_depth) WHERE base=table_id; - SELECT jsonb_agg(to_jsonb(jt_cte.*)) INTO joinable_tables FROM jt_cte; - WITH target_cte AS ( + WITH jt_cte AS ( + SELECT * FROM msar.get_joinable_tables(max_depth) WHERE base=table_id + ), target_cte AS ( SELECT pga.attrelid AS tt_oid, jsonb_build_object( 'name', msar.get_relation_name(pga.attrelid), @@ -153,15 +149,16 @@ BEGIN ) ) ) AS tt_info - FROM pg_catalog.pg_attribute AS pga - LEFT JOIN jt_cte ON pga.attrelid = jt_cte.target + FROM pg_catalog.pg_attribute AS pga, jt_cte WHERE pga.attrelid=jt_cte.target AND pga.attnum > 0 and NOT pga.attisdropped GROUP BY pga.attrelid + ), joinable_tables AS ( + SELECT jsonb_agg(to_jsonb(jt_cte.*)) AS jt FROM jt_cte + ), target_table_info AS ( + SELECT jsonb_object_agg(tt_oid, tt_info) AS tt FROM target_cte ) - SELECT jsonb_object_agg(tt_oid, tt_info) INTO target_table_info FROM target_cte; - RETURN jsonb_build_object( - 'joinable_tables', joinable_tables, - 'target_table_info', target_table_info - ); -END; -$$ LANGUAGE plpgsql RETURNS NULL ON NULL INPUT; + SELECT jsonb_build_object( + 'joinable_tables', joinable_tables.jt, + 'target_table_info', target_table_info.tt + ) FROM joinable_tables, target_table_info; +$$ LANGUAGE SQL STABLE RETURNS NULL ON NULL INPUT; From 2aa5d04ecf0a7633c9d8b08c05ade4dff025a41d Mon Sep 17 00:00:00 2001 From: Anish Umale Date: Thu, 1 Aug 2024 19:57:45 +0530 Subject: [PATCH 6/6] change test to conform to new response structure --- mathesar/tests/rpc/tables/test_t_base.py | 120 +++++++---------------- 1 file changed, 36 insertions(+), 84 deletions(-) diff --git a/mathesar/tests/rpc/tables/test_t_base.py b/mathesar/tests/rpc/tables/test_t_base.py index 082315b783..714c1e4af3 100644 --- a/mathesar/tests/rpc/tables/test_t_base.py +++ b/mathesar/tests/rpc/tables/test_t_base.py @@ -298,22 +298,6 @@ def mock_list_joinable_tables(_table_oid, conn, max_depth): 'join_path': [[[2254329, 2], [2254334, 1]]], 'multiple_results': False }, - { - 'base': 2254329, - 'depth': 1, - 'target': 2254334, - 'fkey_path': [[2254406, False]], - 'join_path': [[[2254329, 2], [2254334, 1]]], - 'multiple_results': False - }, - { - 'base': 2254329, - 'depth': 1, - 'target': 2254350, - 'fkey_path': [[2254411, False]], - 'join_path': [[[2254329, 3], [2254350, 1]]], - 'multiple_results': False - }, { 'base': 2254329, 'depth': 1, @@ -322,33 +306,25 @@ def mock_list_joinable_tables(_table_oid, conn, max_depth): 'join_path': [[[2254329, 3], [2254350, 1]]], 'multiple_results': False }], - 'target_table_info': [ - { - '2254334': 'Items', - 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, - {'2': {'name': 'Barcode', 'type': 'text'}}, - {'3': {'name': 'Acquisition Date', 'type': 'date'}}, - {'5': {'name': 'Book', 'type': 'integer'}}] - }, - { - '2254350': 'Patrons', - 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, - {'2': {'name': 'First Name', 'type': 'text'}}, - {'3': {'name': 'Last Name', 'type': 'text'}}] - }, - { - '2254334': 'Items', - 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, - {'2': {'name': 'Barcode', 'type': 'text'}}, - {'3': {'name': 'Acquisition Date', 'type': 'date'}}, - {'5': {'name': 'Book', 'type': 'integer'}}] + 'target_table_info': { + '2254334': { + 'name': 'Items', + 'columns': { + '1': {'name': 'id', 'type': 'integer'}, + '2': {'name': 'Barcode', 'type': 'text'}, + '3': {'name': 'Acquisition Date', 'type': 'date'}, + '5': {'name': 'Book', 'type': 'integer'} + } }, - { - '2254350': 'Patrons', - 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, - {'2': {'name': 'First Name', 'type': 'text'}}, - {'3': {'name': 'Last Name', 'type': 'text'}}] - }] + '2254350': { + 'name': 'Patrons', + 'columns': { + '1': {'name': 'id', 'type': 'integer'}, + '2': {'name': 'First Name', 'type': 'text'}, + '3': {'name': 'Last Name', 'type': 'text'} + } + } + } } expected_dict = { 'joinable_tables': [ @@ -360,22 +336,6 @@ def mock_list_joinable_tables(_table_oid, conn, max_depth): 'join_path': [[[2254329, 2], [2254334, 1]]], 'multiple_results': False }, - { - 'base': 2254329, - 'depth': 1, - 'target': 2254334, - 'fkey_path': [[2254406, False]], - 'join_path': [[[2254329, 2], [2254334, 1]]], - 'multiple_results': False - }, - { - 'base': 2254329, - 'depth': 1, - 'target': 2254350, - 'fkey_path': [[2254411, False]], - 'join_path': [[[2254329, 3], [2254350, 1]]], - 'multiple_results': False - }, { 'base': 2254329, 'depth': 1, @@ -384,33 +344,25 @@ def mock_list_joinable_tables(_table_oid, conn, max_depth): 'join_path': [[[2254329, 3], [2254350, 1]]], 'multiple_results': False }], - 'target_table_info': [ - { - '2254334': 'Items', - 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, - {'2': {'name': 'Barcode', 'type': 'text'}}, - {'3': {'name': 'Acquisition Date', 'type': 'date'}}, - {'5': {'name': 'Book', 'type': 'integer'}}] - }, - { - '2254350': 'Patrons', - 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, - {'2': {'name': 'First Name', 'type': 'text'}}, - {'3': {'name': 'Last Name', 'type': 'text'}}] + 'target_table_info': { + '2254334': { + 'name': 'Items', + 'columns': { + '1': {'name': 'id', 'type': 'integer'}, + '2': {'name': 'Barcode', 'type': 'text'}, + '3': {'name': 'Acquisition Date', 'type': 'date'}, + '5': {'name': 'Book', 'type': 'integer'} + } }, - { - '2254334': 'Items', - 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, - {'2': {'name': 'Barcode', 'type': 'text'}}, - {'3': {'name': 'Acquisition Date', 'type': 'date'}}, - {'5': {'name': 'Book', 'type': 'integer'}}] - }, - { - '2254350': 'Patrons', - 'columns': [{'1': {'name': 'id', 'type': 'integer'}}, - {'2': {'name': 'First Name', 'type': 'text'}}, - {'3': {'name': 'Last Name', 'type': 'text'}}] - }] + '2254350': { + 'name': 'Patrons', + 'columns': { + '1': {'name': 'id', 'type': 'integer'}, + '2': {'name': 'First Name', 'type': 'text'}, + '3': {'name': 'Last Name', 'type': 'text'} + } + } + } } monkeypatch.setattr(tables.base, 'connect', mock_connect) monkeypatch.setattr(tables.base, 'list_joinable_tables', mock_list_joinable_tables)