From 0fb77712e334670530d544d21395179ac5ed9465 Mon Sep 17 00:00:00 2001 From: Brent Moran Date: Fri, 1 Nov 2024 15:05:54 +0800 Subject: [PATCH 01/12] change format for record lister when no privilege --- db/sql/00_msar.sql | 6 +++--- db/sql/test_00_msar.sql | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/db/sql/00_msar.sql b/db/sql/00_msar.sql index 5159c9d6e6..718eb71cf0 100644 --- a/db/sql/00_msar.sql +++ b/db/sql/00_msar.sql @@ -5123,15 +5123,15 @@ BEGIN LEFT JOIN groups_cte ON enriched_results_cte.__mathesar_gid = groups_cte.id %14$s CROSS JOIN count_cte $q$, - /* %1 */ msar.build_selectable_column_expr(tab_id), + /* %1 */ COALESCE(msar.build_selectable_column_expr(tab_id), 'NULL'), /* %2 */ msar.get_relation_schema_name(tab_id), /* %3 */ msar.get_relation_name(tab_id), /* %4 */ limit_, /* %5 */ offset_, /* %6 */ msar.build_order_by_expr(tab_id, order_), /* %7 */ msar.build_where_clause(tab_id, filter_), - /* %8 */ msar.build_grouping_expr(tab_id, group_), - /* %9 */ msar.build_results_jsonb_expr(tab_id, 'enriched_results_cte', order_), + /* %8 */ COALESCE(msar.build_grouping_expr(tab_id, group_), 'NULL'), + /* %9 */ COALESCE(msar.build_results_jsonb_expr(tab_id, 'enriched_results_cte', order_), 'NULL'), /* %10 */ COALESCE( msar.build_grouping_results_jsonb_expr(tab_id, 'groups_cte', group_), 'NULL' diff --git a/db/sql/test_00_msar.sql b/db/sql/test_00_msar.sql index b86d694d42..f59cdb71b0 100644 --- a/db/sql/test_00_msar.sql +++ b/db/sql/test_00_msar.sql @@ -3249,6 +3249,20 @@ BEGIN ) ) ); + SET ROLE NONE; + CREATE ROLE intern_no_access; + GRANT USAGE ON SCHEMA msar, __msar TO intern_no_access; + GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA msar, __msar TO intern_no_access; + SET ROLE intern_no_access; + RETURN NEXT throws_ok( + format( + 'SELECT msar.list_records_from_table(%s, null, null, null, null, null);', + rel_id + ), + '42501', + 'permission denied for table atable', + 'Records lister throws permission error' + ); END; $$ LANGUAGE plpgsql; From bcdd03991d3fd2a4c9c3e1d70e50bcaa810ae00a Mon Sep 17 00:00:00 2001 From: Brent Moran Date: Mon, 4 Nov 2024 09:14:12 +0800 Subject: [PATCH 02/12] add some tests specifying current record lister behavior --- db/sql/test_00_msar.sql | 221 +++++++++++++++++++++++++++------------- 1 file changed, 148 insertions(+), 73 deletions(-) diff --git a/db/sql/test_00_msar.sql b/db/sql/test_00_msar.sql index f59cdb71b0..10698a8dad 100644 --- a/db/sql/test_00_msar.sql +++ b/db/sql/test_00_msar.sql @@ -3190,79 +3190,6 @@ BEGIN ) ) ); - CREATE ROLE intern_no_pkey; - GRANT USAGE ON SCHEMA msar, __msar TO intern_no_pkey; - GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA msar, __msar TO intern_no_pkey; - GRANT SELECT (col1, col2, col3, col4) ON TABLE atable TO intern_no_pkey; - SET ROLE intern_no_pkey; - RETURN NEXT is( - msar.list_records_from_table( - tab_id => rel_id, - limit_ => null, - offset_ => null, - order_ => null, - filter_ => null, - group_ => null - ), - $j${ - "count": 3, - "results": [ - {"2": 2, "3": "abcde", "4": {"k": 3242348}, "5": true}, - {"2": 5, "3": "sdflkj", "4": "s", "5": {"a": "val"}}, - {"2": 34, "3": "sdflfflsk", "4": null, "5": [1, 2, 3, 4]} - ], - "grouping": null, - "linked_record_summaries": null, - "record_summaries": null - }$j$ || jsonb_build_object( - 'query', concat( - 'SELECT msar.format_data(col1) AS "2", msar.format_data(col2) AS "3",', - ' msar.format_data(col3) AS "4", msar.format_data(col4) AS "5" FROM public.atable', - ' ORDER BY "2" ASC, "3" ASC, "5" ASC LIMIT NULL OFFSET NULL' - ) - ) - ); - RETURN NEXT is( - msar.list_records_from_table( - tab_id => rel_id, - limit_ => null, - offset_ => null, - order_ => '[{"attnum": 3, "direction": "desc"}]', - filter_ => null, - group_ => null - ), - $j${ - "count": 3, - "results": [ - {"2": 5, "3": "sdflkj", "4": "s", "5": {"a": "val"}}, - {"2": 34, "3": "sdflfflsk", "4": null, "5": [1, 2, 3, 4]}, - {"2": 2, "3": "abcde", "4": {"k": 3242348}, "5": true} - ], - "grouping": null, - "linked_record_summaries": null, - "record_summaries": null - }$j$ || jsonb_build_object( - 'query', concat( - 'SELECT msar.format_data(col1) AS "2", msar.format_data(col2) AS "3",', - ' msar.format_data(col3) AS "4", msar.format_data(col4) AS "5" FROM public.atable', - ' ORDER BY "3" DESC, "2" ASC, "3" ASC, "5" ASC LIMIT NULL OFFSET NULL' - ) - ) - ); - SET ROLE NONE; - CREATE ROLE intern_no_access; - GRANT USAGE ON SCHEMA msar, __msar TO intern_no_access; - GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA msar, __msar TO intern_no_access; - SET ROLE intern_no_access; - RETURN NEXT throws_ok( - format( - 'SELECT msar.list_records_from_table(%s, null, null, null, null, null);', - rel_id - ), - '42501', - 'permission denied for table atable', - 'Records lister throws permission error' - ); END; $$ LANGUAGE plpgsql; @@ -5598,3 +5525,151 @@ BEGIN ); END; $$ LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION test_table_select_permissions() RETURNS SETOF TEXT AS $$/* +This test is to check behavior whenever we're selecting from a user table where the calling user has +SELECT permissions on some proper subset. + +- when a user has SELECT on some, but not all, columns we should return results only for the + columns for which they have access. +- when a user doesn't have SELECT on any columns of a table, we should raise a permissions error. +*/ +DECLARE + rel_id oid; +BEGIN + PERFORM __setup_list_records_table(); + rel_id := 'atable'::regclass::oid; + + CREATE ROLE intern_no_pkey; + GRANT USAGE ON SCHEMA msar, __msar TO intern_no_pkey; + GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA msar, __msar TO intern_no_pkey; + GRANT SELECT ON ALL TABLES IN SCHEMA msar, __msar TO intern_no_pkey; + GRANT SELECT (col1, col2, col3, col4) ON TABLE atable TO intern_no_pkey; + SET ROLE intern_no_pkey; + RETURN NEXT is( + msar.list_records_from_table( + tab_id => rel_id, + limit_ => null, + offset_ => null, + order_ => null, + filter_ => null, + group_ => null + ) -> 'results', + $j$[ + {"2": 2, "3": "abcde", "4": {"k": 3242348}, "5": true}, + {"2": 5, "3": "sdflkj", "4": "s", "5": {"a": "val"}}, + {"2": 34, "3": "sdflfflsk", "4": null, "5": [1, 2, 3, 4]} + ]$j$, + 'Results should not have column 1, and should be ordered by remaining columns' + ); + RETURN NEXT is( + msar.list_records_from_table( + tab_id => rel_id, + limit_ => null, + offset_ => null, + order_ => '[{"attnum": 3, "direction": "desc"}]', + filter_ => null, + group_ => null + ) -> 'results', + $j$[ + {"2": 5, "3": "sdflkj", "4": "s", "5": {"a": "val"}}, + {"2": 34, "3": "sdflfflsk", "4": null, "5": [1, 2, 3, 4]}, + {"2": 2, "3": "abcde", "4": {"k": 3242348}, "5": true} + ]$j$, + 'Results should not have a column 1, and ordering spec should work' + ); + RETURN NEXT is( + msar.list_records_from_table( + tab_id => rel_id, + limit_ => null, + offset_ => null, + order_ => '[{"attnum": 1, "direction": "asc"}]', + filter_ => null, + group_ => null + ) -> 'results', + $j$[ + {"2": 2, "3": "abcde", "4": {"k": 3242348}, "5": true}, + {"2": 5, "3": "sdflkj", "4": "s", "5": {"a": "val"}}, + {"2": 34, "3": "sdflfflsk", "4": null, "5": [1, 2, 3, 4]} + ]$j$, + 'specifying that you want to order by a column without permissions is ignored' + ); + RETURN NEXT is( + msar.list_records_from_table( + tab_id => rel_id, + limit_ => null, + offset_ => null, + order_ => '[{"attnum": 1, "direction": "asc", "attnum": 3, "direction": "desc"}]', + filter_ => null, + group_ => null + ) -> 'results', + $j$[ + {"2": 5, "3": "sdflkj", "4": "s", "5": {"a": "val"}}, + {"2": 34, "3": "sdflfflsk", "4": null, "5": [1, 2, 3, 4]}, + {"2": 2, "3": "abcde", "4": {"k": 3242348}, "5": true} + ]$j$, + 'ignore order by column without permissions, use one with permissions' + ); + RETURN NEXT is( + msar.list_records_from_table( + tab_id => rel_id, + limit_ => null, + offset_ => null, + order_ => null, + filter_ => jsonb_build_object( + 'type', 'equal', 'args', jsonb_build_array( + jsonb_build_object('type', 'attnum', 'value', 2), + jsonb_build_object('type', 'literal', 'value', 2) + ) + ), + group_ => null + ) -> 'results', + $j$[ + {"2": 2, "3": "abcde", "4": {"k": 3242348}, "5": true} + ]$j$, + 'filtering without specifying column without permissions works' + ); + + + RETURN NEXT throws_ok( + format( + $s$SELECT + msar.list_records_from_table( + tab_id => %s, + limit_ => null, + offset_ => null, + order_ => null, + filter_ => %L, + group_ => null + ); + $s$, + rel_id, + jsonb_build_object( + 'type', 'equal', 'args', jsonb_build_array( + jsonb_build_object('type', 'attnum', 'value', 1), + jsonb_build_object('type', 'literal', 'value', 2) + ) + ) + ), + '42501', + 'permission denied for table atable', + 'Records lister throws permission error when filtering on column without privilege' + ); + + SET ROLE NONE; + CREATE ROLE intern_no_access; + GRANT USAGE ON SCHEMA msar, __msar TO intern_no_access; + GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA msar, __msar TO intern_no_access; + SET ROLE intern_no_access; + RETURN NEXT throws_ok( + format( + 'SELECT msar.list_records_from_table(%s, null, null, null, null, null);', + rel_id + ), + '42501', + 'permission denied for table atable', + 'Records lister throws permission error' + ); +END; +$$ LANGUAGE plpgsql; From 27eeed33e7a32a5f8f7b60374e0e21e7ae5b716f Mon Sep 17 00:00:00 2001 From: Brent Moran Date: Mon, 4 Nov 2024 17:35:30 +0800 Subject: [PATCH 03/12] add tests for privilege behavior for grouping, summaries --- db/sql/test_00_msar.sql | 102 ++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 30 deletions(-) diff --git a/db/sql/test_00_msar.sql b/db/sql/test_00_msar.sql index 10698a8dad..6b23168c4c 100644 --- a/db/sql/test_00_msar.sql +++ b/db/sql/test_00_msar.sql @@ -4109,34 +4109,6 @@ END; $$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION test_delete_records_from_table_no_pkey() RETURNS SETOF TEXT AS $$ -DECLARE - rel_id oid; - delete_result integer; -BEGIN - PERFORM __setup_list_records_table(); - rel_id := 'atable'::regclass::oid; - CREATE ROLE intern_no_pkey; - GRANT USAGE ON SCHEMA msar, __msar TO intern_no_pkey; - GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA msar, __msar TO intern_no_pkey; - GRANT SELECT ON ALL TABLES IN SCHEMA msar, __msar TO INTERN_no_pkey; - GRANT SELECT (col1, col2, col3, col4) ON TABLE atable TO intern_no_pkey; - SET ROLE intern_no_pkey; - RETURN NEXT throws_ok( - format('SELECT msar.delete_records_from_table(%s, ''[2, 3]'')', rel_id), - '42501', - 'permission denied for table atable', - 'Throw error when trying to delete without permission' - ); - SET ROLE NONE; - RETURN NEXT results_eq( - 'SELECT id FROM atable ORDER BY id', - $v$VALUES ('1'::integer), ('2'::integer), ('3'::integer)$v$ - ); -END; -$$ LANGUAGE plpgsql; - - CREATE OR REPLACE FUNCTION test_delete_records_from_table_stringy_pkey() RETURNS SETOF TEXT AS $$ DECLARE rel_id oid; @@ -5537,8 +5509,10 @@ SELECT permissions on some proper subset. */ DECLARE rel_id oid; + jsonb_result jsonb; BEGIN PERFORM __setup_list_records_table(); + PERFORM __setup_preview_fkey_cols(); rel_id := 'atable'::regclass::oid; CREATE ROLE intern_no_pkey; @@ -5630,8 +5604,6 @@ BEGIN ]$j$, 'filtering without specifying column without permissions works' ); - - RETURN NEXT throws_ok( format( $s$SELECT @@ -5656,10 +5628,71 @@ BEGIN 'permission denied for table atable', 'Records lister throws permission error when filtering on column without privilege' ); + RETURN NEXT is( + msar.list_records_from_table( + tab_id => rel_id, + limit_ => null, + offset_ => null, + order_ => '[{"attnum": 3, "direction": "asc"}, {"attnum": 1, "direction": "asc"}]', + filter_ => null, + group_ => '{"columns": [3, 1]}' + ) -> 'grouping', + $j${ + "groups": [ + {"id": 1, "count": 1, "results_eq": {"3": "abcde"}, "result_indices": [0]}, + {"id": 2, "count": 1, "results_eq": {"3": "sdflfflsk"}, "result_indices": [1]}, + {"id": 3, "count": 1, "results_eq": {"3": "sdflkj"}, "result_indices": [2]} + ], + "columns": [3, 1], + "preproc": null + }$j$, + 'ignore group column without permissions, use one with permissions' + ); + RETURN NEXT throws_ok( + format('SELECT msar.delete_records_from_table(%s, ''[2, 3]'')', rel_id), + '42501', + 'permission denied for table atable', + 'Throw error when trying to delete without permission' + ); + + SET ROLE NONE; + CREATE ROLE intern_students_only; + GRANT USAGE ON SCHEMA msar, __msar TO intern_students_only; + GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA msar, __msar TO intern_students_only; + GRANT SELECT ON ALL TABLES IN SCHEMA msar, __msar TO intern_students_only; + GRANT SELECT ON TABLE "Students" TO intern_students_only; + SET ROLE intern_students_only; + jsonb_result = msar.get_record_from_table( + tab_id => '"Students"'::regclass::oid, + rec_id => 4 + ); + RETURN NEXT is( + jsonb_result -> 'results', + '[{"1": 4, "2": 2.345, "3": 1, "4": "Ida Idalia", "5": 90, "6": "iidalia@example.edu"}]', + 'Record results work when no access to linked table' + ); + RETURN NEXT is( + jsonb_result -> 'linked_record_summaries', + 'null', + 'Record summaries are ignored when no access to linked tables' + ); + RETURN NEXT is( + msar.get_record_from_table( + tab_id => '"Students"'::regclass::oid, + rec_id => 4, + table_record_summary_templates => jsonb_build_object( + '"Teachers"'::regclass::oid, + '[[3], " / ", [2, 2]]'::jsonb + ) + ) -> 'results', + '[{"1": 4, "2": 2.345, "3": 1, "4": "Ida Idalia", "5": 90, "6": "iidalia@example.edu"}]', + 'Record results work when no access to linked table having custom summary' + ); SET ROLE NONE; CREATE ROLE intern_no_access; GRANT USAGE ON SCHEMA msar, __msar TO intern_no_access; + GRANT SELECT ON ALL TABLES IN SCHEMA msar, __msar TO intern_no_access; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA msar, __msar TO intern_no_access; SET ROLE intern_no_access; RETURN NEXT throws_ok( @@ -5671,5 +5704,14 @@ BEGIN 'permission denied for table atable', 'Records lister throws permission error' ); + RETURN NEXT throws_ok( + format( + 'SELECT msar.get_record_from_table(%s, 1, true);', + rel_id + ), + '42501', + 'permission denied for table atable', + 'Records getter throws permission error' + ); END; $$ LANGUAGE plpgsql; From 8e51f71c8a21b87c2b422a3a9508c4c13f57bcfd Mon Sep 17 00:00:00 2001 From: Brent Moran Date: Mon, 4 Nov 2024 17:37:02 +0800 Subject: [PATCH 04/12] add test showing weird summary privilege behavior --- db/sql/test_00_msar.sql | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/db/sql/test_00_msar.sql b/db/sql/test_00_msar.sql index 6b23168c4c..fac85eff0b 100644 --- a/db/sql/test_00_msar.sql +++ b/db/sql/test_00_msar.sql @@ -5688,6 +5688,23 @@ BEGIN '[{"1": 4, "2": 2.345, "3": 1, "4": "Ida Idalia", "5": 90, "6": "iidalia@example.edu"}]', 'Record results work when no access to linked table having custom summary' ); + RETURN NEXT throws_ok( + format( + $s$SELECT msar.get_record_from_table( + tab_id => %1$s, + rec_id => 4, + return_record_summaries => true, + table_record_summary_templates => jsonb_build_object( + %1$s, + '[[4], " ", [5], "%% - (", [3, 3], " / ", [3, 2, 2], ")"]'::jsonb + ) + );$s$, + '"Students"'::regclass::oid + ), + '42501', + 'permission denied for table Teachers', + 'Record summary throws permission error when linked table missing privileges -- TODO' + ); SET ROLE NONE; CREATE ROLE intern_no_access; From 9595f616ade0e6b36ee3c0d995513a7caad43f92 Mon Sep 17 00:00:00 2001 From: Brent Moran Date: Mon, 4 Nov 2024 23:38:03 +0800 Subject: [PATCH 05/12] modify table patcher to return correct permission error --- db/sql/00_msar.sql | 7 +++---- db/sql/test_00_msar.sql | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/db/sql/00_msar.sql b/db/sql/00_msar.sql index 718eb71cf0..24d1e83363 100644 --- a/db/sql/00_msar.sql +++ b/db/sql/00_msar.sql @@ -5443,8 +5443,8 @@ DECLARE BEGIN EXECUTE format( $p$ - WITH update_cte AS (%1$s %2$s RETURNING %3$s) - SELECT msar.format_data(%4$I)::text FROM update_cte + WITH update_cte AS (%1$s %2$s RETURNING %3$I) + SELECT * FROM update_cte $p$, msar.build_update_expr(tab_id, rec_def), msar.build_where_clause( @@ -5455,8 +5455,7 @@ BEGIN ) ) ), - msar.build_selectable_column_expr(tab_id), - msar.get_pk_column(tab_id) + msar.get_column_name(tab_id, msar.get_pk_column(tab_id)) ) INTO rec_modified_id; rec_modified := msar.get_record_from_table( tab_id, diff --git a/db/sql/test_00_msar.sql b/db/sql/test_00_msar.sql index fac85eff0b..4e8fcd5cf0 100644 --- a/db/sql/test_00_msar.sql +++ b/db/sql/test_00_msar.sql @@ -5652,7 +5652,20 @@ BEGIN format('SELECT msar.delete_records_from_table(%s, ''[2, 3]'')', rel_id), '42501', 'permission denied for table atable', - 'Throw error when trying to delete without permission' + 'Throw error when trying to delete without SELECT on id' + ); + RETURN NEXT throws_ok( + format( + $s$SELECT msar.patch_record_in_table( + tab_id => %s, + rec_id => 1, + rec_def => '{"2": 10}'::jsonb + );$s$, + rel_id + ), + '42501', + 'permission denied for table atable', + 'Records patcher throws permission error when trying to patch without SELECT on id' ); SET ROLE NONE; From 0abf434be19da53e8cddd8fb6b4cca7ead6ed4c8 Mon Sep 17 00:00:00 2001 From: Brent Moran Date: Tue, 5 Nov 2024 13:35:27 +0800 Subject: [PATCH 06/12] modify record adder to return correct permission error --- db/sql/00_msar.sql | 5 ++--- db/sql/test_00_msar.sql | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/db/sql/00_msar.sql b/db/sql/00_msar.sql index 24d1e83363..fa2acd91ad 100644 --- a/db/sql/00_msar.sql +++ b/db/sql/00_msar.sql @@ -5380,12 +5380,11 @@ BEGIN EXECUTE format( $q$ WITH insert_cte AS (%1$s RETURNING %2$s) - SELECT msar.format_data(%3$I)::text + SELECT * FROM insert_cte $q$, /* %1 */ msar.build_single_insert_expr(tab_id, rec_def), - /* %2 */ msar.build_selectable_column_expr(tab_id), - /* %3 */ msar.get_pk_column(tab_id) + /* %2 */ msar.get_column_name(tab_id, msar.get_pk_column(tab_id)) ) INTO rec_created_id; rec_created := msar.get_record_from_table( tab_id, diff --git a/db/sql/test_00_msar.sql b/db/sql/test_00_msar.sql index 4e8fcd5cf0..cf80a88b2b 100644 --- a/db/sql/test_00_msar.sql +++ b/db/sql/test_00_msar.sql @@ -5665,9 +5665,23 @@ BEGIN ), '42501', 'permission denied for table atable', - 'Records patcher throws permission error when trying to patch without SELECT on id' + 'Records patcher throws permission error when trying to patch without SELECT on pkey' ); + RETURN NEXT throws_ok( + format( + $s$SELECT msar.add_record_to_table( + tab_id => %s, + rec_def => '{"2": 234, "3": "ab234", "4": {"key": "val"}, "5": {"key2": "val2"}}'::jsonb + );$s$, + rel_id + ), + '42501', + 'permission denied for table atable', + 'Record adder throws permission error when adding a record without SELECT on pkey' + ); + + SET ROLE NONE; CREATE ROLE intern_students_only; GRANT USAGE ON SCHEMA msar, __msar TO intern_students_only; From 30747c55afa6d68dad97ec68347d5bafbdb46bb8 Mon Sep 17 00:00:00 2001 From: Brent Moran Date: Tue, 5 Nov 2024 15:14:24 +0800 Subject: [PATCH 07/12] modify record search to return correct permission error --- db/sql/00_msar.sql | 15 ++++++---- db/sql/test_00_msar.sql | 61 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/db/sql/00_msar.sql b/db/sql/00_msar.sql index fa2acd91ad..f5384ab839 100644 --- a/db/sql/00_msar.sql +++ b/db/sql/00_msar.sql @@ -5220,7 +5220,7 @@ BEGIN SELECT count(1) AS count FROM %2$I.%3$I %4$s ), results_cte AS ( - SELECT %1$s FROM %2$I.%3$I %4$s ORDER BY %6$s LIMIT %5$L + SELECT %1$s FROM %2$I.%3$I %4$s %6$s LIMIT %5$L ), summary_cte_self AS (%7$s) %8$s SELECT jsonb_build_object( @@ -5228,19 +5228,22 @@ BEGIN 'count', coalesce(max(count_cte.count), 0), 'linked_record_summaries', %10$s, 'record_summaries', %11$s, - 'query', $iq$SELECT %1$s FROM %2$I.%3$I %4$s ORDER BY %6$s LIMIT %5$L$iq$ + 'query', $iq$SELECT %1$s FROM %2$I.%3$I %4$s %6$s LIMIT %5$L$iq$ ) FROM results_cte %9$s CROSS JOIN count_cte $q$, - /* %1 */ msar.build_selectable_column_expr(tab_id), + /* %1 */ COALESCE(msar.build_selectable_column_expr(tab_id), 'NULL'), /* %2 */ msar.get_relation_schema_name(tab_id), /* %3 */ msar.get_relation_name(tab_id), /* %4 */ 'WHERE ' || msar.get_score_expr(tab_id, search_) || ' > 0', /* %5 */ limit_, - /* %6 */ concat( - msar.get_score_expr(tab_id, search_) || ' DESC, ', - msar.build_total_order_expr(tab_id, null) + /* %6 */ 'ORDER BY ' || NULLIF( + concat( + msar.get_score_expr(tab_id, search_) || ' DESC, ', + msar.build_total_order_expr(tab_id, null) + ), + '' ), /* %7 */ msar.build_record_summary_query_for_table( tab_id, diff --git a/db/sql/test_00_msar.sql b/db/sql/test_00_msar.sql index cf80a88b2b..98e7877c14 100644 --- a/db/sql/test_00_msar.sql +++ b/db/sql/test_00_msar.sql @@ -3828,14 +3828,14 @@ $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION __setup_search_records_table() RETURNS SETOF TEXT AS $$ BEGIN - CREATE TABLE atable ( + CREATE TABLE search_table ( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, col1 integer, col2 varchar, coltodrop integer ); - ALTER TABLE atable DROP COLUMN coltodrop; - INSERT INTO atable (col1, col2) VALUES + ALTER TABLE search_table DROP COLUMN coltodrop; + INSERT INTO search_table (col1, col2) VALUES (1, 'bcdea'), (12, 'vwxyz'), (1, 'edcba'), @@ -3850,7 +3850,7 @@ DECLARE search_result jsonb; BEGIN PERFORM __setup_search_records_table(); - rel_id := 'atable'::regclass::oid; + rel_id := 'search_table'::regclass::oid; search_result := msar.search_records_from_table( rel_id, jsonb_build_array( @@ -5513,6 +5513,7 @@ DECLARE BEGIN PERFORM __setup_list_records_table(); PERFORM __setup_preview_fkey_cols(); + PERFORM __setup_search_records_table(); rel_id := 'atable'::regclass::oid; CREATE ROLE intern_no_pkey; @@ -5520,6 +5521,7 @@ BEGIN GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA msar, __msar TO intern_no_pkey; GRANT SELECT ON ALL TABLES IN SCHEMA msar, __msar TO intern_no_pkey; GRANT SELECT (col1, col2, col3, col4) ON TABLE atable TO intern_no_pkey; + GRANT SELECT (col1, col2) ON TABLE search_table TO intern_no_pkey; SET ROLE intern_no_pkey; RETURN NEXT is( msar.list_records_from_table( @@ -5648,12 +5650,44 @@ BEGIN }$j$, 'ignore group column without permissions, use one with permissions' ); + + RETURN NEXT is( + msar.search_records_from_table( + 'search_table'::regclass::oid, + jsonb_build_array( + jsonb_build_object('attnum', 3, 'literal', 'bc') + ), + null + ) -> 'results', + jsonb_build_array( + jsonb_build_object('2', 1, '3', 'bcdea'), + jsonb_build_object('2', 2, '3', 'abcde') + ), + 'search ignores unspecified columns without permissions' + ); + RETURN NEXT is( + msar.search_records_from_table( + 'search_table'::regclass::oid, + jsonb_build_array( + jsonb_build_object('attnum', 1, 'literal', 2), + jsonb_build_object('attnum', 3, 'literal', 'bc') + ), + null + ) -> 'results', + jsonb_build_array( + jsonb_build_object('2', 1, '3', 'bcdea'), + jsonb_build_object('2', 2, '3', 'abcde') + ), + 'search ignores specified columns without permissions, uses other' + ); + RETURN NEXT throws_ok( format('SELECT msar.delete_records_from_table(%s, ''[2, 3]'')', rel_id), '42501', 'permission denied for table atable', 'Throw error when trying to delete without SELECT on id' ); + RETURN NEXT throws_ok( format( $s$SELECT msar.patch_record_in_table( @@ -5757,5 +5791,24 @@ BEGIN 'permission denied for table atable', 'Records getter throws permission error' ); + + RETURN NEXT throws_ok( + format( + 'SELECT msar.search_records_from_table(%s, ''[{"attnum": 3, "literal": "bc"}]'', null);', + 'search_table'::regclass::oid + ), + '42501', + 'permission denied for table search_table', + 'Records search throws permission error with nonempty search terms' + ); + RETURN NEXT throws_ok( + format( + 'SELECT msar.search_records_from_table(%s, ''[]'', null);', + 'search_table'::regclass::oid + ), + '42501', + 'permission denied for table search_table', + 'Records search throws permission error with empty search terms' + ); END; $$ LANGUAGE plpgsql; From a6c2bf7f2c7824d192bb6f6a26d292831e73e298 Mon Sep 17 00:00:00 2001 From: Sean Colsen Date: Tue, 5 Nov 2024 15:22:27 -0500 Subject: [PATCH 08/12] Add show_functions.sh helper script --- db/sql/show_functions.sh | 4 ++++ 1 file changed, 4 insertions(+) create mode 100755 db/sql/show_functions.sh diff --git a/db/sql/show_functions.sh b/db/sql/show_functions.sh new file mode 100755 index 0000000000..195cc2f263 --- /dev/null +++ b/db/sql/show_functions.sh @@ -0,0 +1,4 @@ +# Print all Mathesar functions to the console for easy grepping + +docker exec -it mathesar_dev_db bash -c "PAGER=cat psql -U mathesar --pset footer=off -qAtz -c $'SELECT DISTINCT routine_schema || \'.\' || routine_name as f FROM information_schema.routines WHERE routine_schema IN (\'msar\', \'__msar\') ORDER BY f;'" + From b9866f879695309a6cdd35c8d779831339bb115b Mon Sep 17 00:00:00 2001 From: Sean Colsen Date: Tue, 5 Nov 2024 16:00:36 -0500 Subject: [PATCH 09/12] Remove some trailing spaces in SQL files --- db/sql/00_msar.sql | 20 ++++++++++---------- db/sql/test_00_msar.sql | 24 ++++++++++++------------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/db/sql/00_msar.sql b/db/sql/00_msar.sql index 5159c9d6e6..33945e8f44 100644 --- a/db/sql/00_msar.sql +++ b/db/sql/00_msar.sql @@ -144,7 +144,7 @@ $$ LANGUAGE sql RETURNS NULL ON NULL INPUT; CREATE OR REPLACE FUNCTION __msar.exec_dql(command text) RETURNS jsonb AS $$/* Execute the given command, returning a JSON object describing the records in the following form: -[ +[ {"id": 1, "col1_name": "value1", "col2_name": "value2"}, {"id": 2, "col1_name": "value1", "col2_name": "value2"}, {"id": 3, "col1_name": "value1", "col2_name": "value2"}, @@ -172,7 +172,7 @@ $$ LANGUAGE plpgsql RETURNS NULL ON NULL INPUT; CREATE OR REPLACE FUNCTION __msar.exec_dql(command_template text, arguments variadic anyarray) RETURNS jsonb AS $$/* Execute a templated command, returning a JSON object describing the records in the following form: -[ +[ {"id": 1, "col1_name": "value1", "col2_name": "value2"}, {"id": 2, "col1_name": "value1", "col2_name": "value2"}, {"id": 3, "col1_name": "value1", "col2_name": "value2"}, @@ -1063,7 +1063,7 @@ SELECT coalesce( ), '[]'::jsonb ) -FROM pg_catalog.pg_class AS pgc +FROM pg_catalog.pg_class AS pgc LEFT JOIN pg_catalog.pg_namespace AS pgn ON pgc.relnamespace = pgn.oid WHERE pgc.relnamespace = sch_id AND pgc.relkind = 'r'; $$ LANGUAGE SQL RETURNS NULL ON NULL INPUT; @@ -1093,7 +1093,7 @@ CREATE OR REPLACE FUNCTION msar.schema_info_table() RETURNS TABLE current_role_owns boolean, -- Whether the current role owns the schema. table_count integer -- The number of tables in the schema. ) AS $$ -SELECT +SELECT s.oid::bigint AS oid, s.nspname AS name, pg_catalog.obj_description(s.oid) AS description, @@ -3144,7 +3144,7 @@ BEGIN relation_name := __msar.get_qualified_relation_name_or_null(tab_id); -- if_exists doesn't work while working with oids because -- the SQL query gets parameterized with tab_id instead of relation_name - -- since we're unable to find the relation_name for a non existing table. + -- since we're unable to find the relation_name for a non existing table. PERFORM __msar.drop_table(relation_name, cascade_, if_exists => false); RETURN relation_name; END; @@ -3449,7 +3449,7 @@ BEGIN ) AS cast_expr FROM jsonb_array_elements(col_cast_def) AS col_cast ) - SELECT + SELECT __msar.exec_dql(sel_query, cast_expr, tab_name, rec_limit::text) INTO records FROM preview_cte; RETURN records; @@ -3883,7 +3883,7 @@ BEGIN END IF; -- Here, we perform all description-changing alterations. - FOR description_alter IN + FOR description_alter IN SELECT (col_alter->>'attnum')::integer AS col_id, col_alter->>'description' AS comment_ @@ -4019,7 +4019,7 @@ msar.add_foreign_key_column( rel_id oid, frel_id oid, unique_link boolean DEFAULT false -) RETURNS smallint AS $$/* +) RETURNS smallint AS $$/* Create a many-to-one or a one-to-one link between tables, returning the attnum of the newly created column, returning the attnum of the added column. @@ -4778,7 +4778,7 @@ CREATE OR REPLACE FUNCTION msar.build_record_summary_query_from_template( Args: tab_id: The OID of the table for which to generate a record summary query. template: A JSON array that represents the record summary template (described in detail below). - + Example template: [ @@ -4798,7 +4798,7 @@ CREATE OR REPLACE FUNCTION msar.build_record_summary_query_from_template( contains more than one column reference, it represents a chain of FK columns starting from the base table and ending with a non-FK column. This function follows the foreign keys to produce the joins. Multi-column FK constraints are not supported. - + Return value: a stringified query which produces a result set matching the structure described in the return value of msar.get_record_summaries_via_query. */ diff --git a/db/sql/test_00_msar.sql b/db/sql/test_00_msar.sql index b86d694d42..e54cee2de4 100644 --- a/db/sql/test_00_msar.sql +++ b/db/sql/test_00_msar.sql @@ -1112,12 +1112,12 @@ $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION __setup_link_tables() RETURNS SETOF TEXT AS $$ BEGIN CREATE TABLE actors (id SERIAL PRIMARY KEY, actor_name text); - INSERT INTO actors(actor_name) VALUES + INSERT INTO actors(actor_name) VALUES ('Cillian Murphy'), ('Leonardo DiCaprio'), ('Margot Robbie'), ('Ryan Gosling'), - ('Ana de Armas'); + ('Ana de Armas'); CREATE TABLE movies (id SERIAL PRIMARY KEY, movie_name text); INSERT INTO movies(movie_name) VALUES ('The Wolf of Wall Street'), @@ -1228,7 +1228,7 @@ CREATE OR REPLACE FUNCTION test_drop_schema_using_name() RETURNS SETOF TEXT AS $ BEGIN PERFORM __setup_drop_schema(); PERFORM msar.drop_schema( - sch_name => 'drop_test_schema', + sch_name => 'drop_test_schema', cascade_ => false ); RETURN NEXT hasnt_schema('drop_test_schema'); @@ -1238,7 +1238,7 @@ BEGIN sch_name => 'drop_non_existing_schema', cascade_ => false ) - $d$, + $d$, '3F000' ); END; @@ -2567,7 +2567,7 @@ BEGIN RETURN NEXT ok(msar.get_valid_target_type_strings('text'::regtype::oid) @> '["numeric", "text"]'); RETURN NEXT is(jsonb_array_length(msar.get_valid_target_type_strings('text'::regtype::oid)), 2); - + RETURN NEXT is(msar.get_valid_target_type_strings('interval'), NULL); END; $$ LANGUAGE plpgsql; @@ -2738,7 +2738,7 @@ BEGIN COMMENT ON TABLE pi.one IS 'first decimal digit of pi'; CREATE SCHEMA alice; - -- No tables in the schema + -- No tables in the schema END; $$ LANGUAGE plpgsql; @@ -3535,7 +3535,7 @@ BEGIN "create_db": false, "create_role": false, "description": null - }, + }, { "oid": %3$s, "name": "parent2", @@ -4055,7 +4055,7 @@ DECLARE BEGIN PERFORM __setup_list_records_table(); rel_id := 'atable'::regclass::oid; - + -- We should be able to retrieve a single record RETURN NEXT is(msar.get_record_from_table(rel_id, 2) -> 'results', record_2_results); @@ -4463,7 +4463,7 @@ CREATE TABLE "Students" ( /* attnum: 6 */ "Email" text ); -INSERT INTO "Counselors" +INSERT INTO "Counselors" ( coid , "Name" , "Email" ) VALUES ( 1.234 , 'Alice Alison' , 'aalison@example.edu' ), ( 2.345 , 'Bob Bobinson' , 'bbobinson@example.edu' ); @@ -4640,7 +4640,7 @@ BEGIN '"Students"'::regclass::oid, '[[4], " ", [5], "% - (", [3, 3], " / ", [3, 2, 2], ")"]'::jsonb ) - ) -> 'record_summaries' ->> '4', + ) -> 'record_summaries' ->> '4', 'Ida Idalia 90% - (Carol Carlson / Alice Alison)' ); END; @@ -4661,7 +4661,7 @@ BEGIN '"Students"'::regclass::oid, '[[4], " ", [5], "% - \"", [3, 3], " <", [3, 4], ">\""]'::jsonb ) - ) -> 'record_summaries' ->> '7', + ) -> 'record_summaries' ->> '7', 'Arnold Baker % - "Neil Smith <>"' ); END; @@ -4684,7 +4684,7 @@ BEGIN '"Students"'::regclass::oid, 'null'::jsonb ) - ) -> 'record_summaries' ->> '2', + ) -> 'record_summaries' ->> '2', 'Gabby Gabberson' ); END; From 6272ebcf12b3c8ce475993e1d472c10116fdc46b Mon Sep 17 00:00:00 2001 From: Sean Colsen Date: Tue, 5 Nov 2024 16:01:56 -0500 Subject: [PATCH 10/12] Add failing test for record summary permissions --- db/sql/test_00_msar.sql | 112 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/db/sql/test_00_msar.sql b/db/sql/test_00_msar.sql index e54cee2de4..43568dba1a 100644 --- a/db/sql/test_00_msar.sql +++ b/db/sql/test_00_msar.sql @@ -4714,6 +4714,118 @@ END; $$ LANGUAGE plpgsql; +CREATE OR REPLACE FUNCTION test_record_summary_with_limited_privileges() RETURNS SETOF TEXT AS $$ +DECLARE result jsonb; +BEGIN + CREATE ROLE roland; + + GRANT USAGE ON SCHEMA __msar, msar TO roland; + GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA msar, __msar TO roland; + GRANT SELECT ON ALL TABLES IN SCHEMA msar, __msar TO roland; + + -- Tables: + -- + -- widget + -- ┣━ doodad + -- ┃ ┗━ frobnicator + -- ┣━ apparatus + -- ┣━ configuration + -- ┗━ projection + -- + -- ✅ = roland can SELECT + -- ❌ = roland cannot SELECT + + CREATE TABLE frobnicator ( + /* ❌ */ id INT PRIMARY KEY, + /* ❌ */ frequency INT + ); + INSERT INTO frobnicator (id, frequency) VALUES (7, 7); + + CREATE TABLE doodad ( + /* ✅ */ id INT PRIMARY KEY, + /* ❌ */ size INT, + /* ✅ */ color TEXT, + /* ✅ */ frobnicator INT REFERENCES frobnicator(id) + ); + INSERT INTO doodad (id, size, color, frobnicator) VALUES (4, 3, 'chartruse', 7); + GRANT SELECT (id, color, frobnicator) ON doodad TO roland; + + CREATE TABLE apparatus ( + /* ✅ */ id INT PRIMARY KEY, + /* ✅ */ phase INT + ); + INSERT INTO apparatus (id, phase) VALUES (9, 4); + GRANT SELECT ON apparatus TO roland; + + CREATE TABLE configuration ( + /* ❌ */ id INT PRIMARY KEY, + /* ❌ */ astral_plane TEXT + ); + INSERT INTO configuration (id, astral_plane) VALUES (13, 'Etheric'); + + CREATE TABLE projection ( + /* ❌ */ id INT PRIMARY KEY, + /* ✅ */ sensitivity INT + ); + INSERT INTO projection (id, sensitivity) VALUES (57, 27); + GRANT SELECT (sensitivity) ON projection TO roland; + + CREATE TABLE widget ( + /* ✅ 1 */ id INT PRIMARY KEY, + /* ✅ 2 */ name TEXT NOT NULL, + /* ✅ 3 */ doodad INT REFERENCES doodad(id), + /* ❌ 4 */ apparatus INT REFERENCES apparatus(id), + /* ✅ 5 */ configuration INT REFERENCES configuration(id), + /* ✅ 6 */ projection INT REFERENCES projection(id) + ); + INSERT INTO widget (id, name, doodad, apparatus, configuration, projection) VALUES + (2, 'wow', 4, 9, 13, 57); + GRANT SELECT (id, name, doodad, configuration, projection) ON widget TO roland; + + SET ROLE roland; + + SELECT msar.get_record_from_table( + tab_id => 'widget'::regclass::oid, + rec_id => 2, + return_record_summaries => true, + table_record_summary_templates => jsonb_build_object( + 'widget'::regclass::oid, + json_build_array( + '/', '[2]'::jsonb, -- ✅ widget.name + '/', '[3, 2]'::jsonb, -- ❌ widget.doodad.size + '/', '[3, 3]'::jsonb, -- ✅ widget.doodad.color + '/', '[3, 4]'::jsonb, -- ✅ widget.doodad.frobnicator + '/', '[3, 4, 1]'::jsonb, -- ❌ widget.doodad.frobnicator.id + '/', '[4]'::jsonb, -- ❌ widget.apparatus + '/', '[4, 2]'::jsonb, -- ❌ widget.apparatus.phase + '/', '[5]'::jsonb, -- ✅ widget.configuration + '/', '[5, 2]'::jsonb, -- ❌ widget.configuration.astral_plane + '/', '[6]'::jsonb, -- ✅ widget.projection + '/', '[6, 2]'::jsonb -- ❌ widget.projection.sensitivity (❌ because can't join) + ) + ) + ) INTO result; + + RETURN NEXT is( + result -> 'record_summaries' ->> '2', + concat( + '/wow', -- ✅ widget.name + '/', -- ❌ widget.doodad.size + '/chartruse', -- ✅ widget.doodad.color + '/7', -- ✅ widget.doodad.frobnicator + '/', -- ❌ widget.doodad.frobnicator.id + '/', -- ❌ widget.apparatus + '/', -- ❌ widget.apparatus.phase + '/13', -- ✅ widget.configuration + '/', -- ❌ widget.configuration.astral_plane + '/57', -- ✅ widget.projection + '/' -- ❌ widget.projection.sensitivity (❌ because can't join) + ) + ); +END; +$$ LANGUAGE plpgsql; + + CREATE OR REPLACE FUNCTION test_add_record_to_table_with_preview() RETURNS SETOF TEXT AS $$ BEGIN PERFORM __setup_preview_fkey_cols(); From 7c48fe9407f062f637cfbd51f0b619d717b6c749 Mon Sep 17 00:00:00 2001 From: Sean Colsen Date: Tue, 5 Nov 2024 16:02:34 -0500 Subject: [PATCH 11/12] Handle inadequate permissions in record summaries --- db/sql/00_msar.sql | 52 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/db/sql/00_msar.sql b/db/sql/00_msar.sql index 33945e8f44..4039d46d5d 100644 --- a/db/sql/00_msar.sql +++ b/db/sql/00_msar.sql @@ -4767,6 +4767,14 @@ LIMIT 1; $$ LANGUAGE SQL STABLE RETURNS NULL ON NULL INPUT; +CREATE OR REPLACE FUNCTION msar.build_empty_record_summary_query() RETURNS TEXT AS $$/* + Returns a stringified query structured consistently with a record summary query but which will + yield no record summaries when run. +*/ + SELECT $q$ SELECT NULL AS key, NULL AS summary WHERE FALSE $q$; +$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; + + CREATE OR REPLACE FUNCTION msar.build_record_summary_query_from_template( tab_id oid, key_col_id smallint, @@ -4814,10 +4822,15 @@ DECLARE join_section text; BEGIN IF key_col_id IS NULL THEN - -- If the key column is NULL, then we can't generate a record summary query. We return a query - -- that will return no rows. - RETURN $q$ SELECT NULL AS key, NULL AS summary WHERE FALSE $q$; + -- If we don't have a key column, then we can't generate a record summary query. + RETURN msar.build_empty_record_summary_query(); + END IF; + + IF NOT pg_catalog.has_column_privilege(tab_id, key_col_id, 'SELECT') THEN + -- If we don't have permission to select the key column, then we can't generate a record + RETURN msar.build_empty_record_summary_query(); END IF; + IF jsonb_typeof(template) <> 'array' THEN RAISE EXCEPTION 'Record summary template must be a JSON array.'; END IF; @@ -4830,7 +4843,8 @@ BEGIN fk_col_id smallint; contextual_tab_id oid := tab_id; prev_alias text := base_alias; - ref_column_name text; + ref_col_id smallint; + ref_col_name text; BEGIN -- Column reference template parts IF ref_chain_length > 0 THEN @@ -4838,15 +4852,20 @@ BEGIN -- columns. FOREACH fk_col_id IN ARRAY ref_chain[1:ref_chain_length-1] LOOP DECLARE - fk_col_name text := msar.get_column_name(contextual_tab_id, fk_col_id); + fk_col_name text; ref_tab_id oid; - ref_col_id smallint; ref_sch_name text; ref_tab_name text; - ref_col_name text; alias text; join_clause text; BEGIN + IF NOT pg_catalog.has_column_privilege(contextual_tab_id, fk_col_id, 'SELECT') THEN + -- Silently ignore FK columns that we don't have permissions to select. + CONTINUE template_parts_loop; + END IF; + + fk_col_name := msar.get_column_name(contextual_tab_id, fk_col_id); + IF fk_col_name IS NULL THEN -- Silently ignore references to non-existing FK columns. This can happen if a column -- has been deleted. @@ -4863,6 +4882,12 @@ BEGIN CONTINUE template_parts_loop; END IF; + IF NOT pg_catalog.has_column_privilege(ref_tab_id, ref_col_id, 'SELECT') THEN + -- Silently ignore FK columns which point to columns that we don't have permission to + -- select. + CONTINUE template_parts_loop; + END IF; + ref_tab_name := msar.get_relation_name(ref_tab_id); ref_sch_name := msar.get_relation_schema_name(ref_tab_id); ref_col_name := msar.get_column_name(ref_tab_id, ref_col_id); @@ -4885,13 +4910,20 @@ BEGIN END; END LOOP; - ref_column_name := msar.get_column_name(contextual_tab_id, ref_chain[ref_chain_length]); - IF ref_column_name IS NOT NULL THEN + ref_col_id := ref_chain[ref_chain_length]; + + IF NOT pg_catalog.has_column_privilege(contextual_tab_id, ref_col_id, 'SELECT') THEN + -- Silently ignore the final column reference if we don't have permission to select it. + CONTINUE template_parts_loop; + END IF; + + ref_col_name := msar.get_column_name(contextual_tab_id, ref_col_id); + IF ref_col_name IS NOT NULL THEN expr_parts := array_append( expr_parts, concat( 'COALESCE(msar.format_data(', - prev_alias, '.', quote_ident(ref_column_name), + prev_alias, '.', quote_ident(ref_col_name), E')::text, \'\')' ) ); From 7b29fe3efa91e38ea44e2c5343d016297c2f3bfb Mon Sep 17 00:00:00 2001 From: Brent Moran Date: Wed, 6 Nov 2024 16:58:25 +0800 Subject: [PATCH 12/12] remove defunct SQL test after merge --- db/sql/test_00_msar.sql | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/db/sql/test_00_msar.sql b/db/sql/test_00_msar.sql index 811ec5acbc..2287d39fad 100644 --- a/db/sql/test_00_msar.sql +++ b/db/sql/test_00_msar.sql @@ -5861,23 +5861,6 @@ BEGIN '[{"1": 4, "2": 2.345, "3": 1, "4": "Ida Idalia", "5": 90, "6": "iidalia@example.edu"}]', 'Record results work when no access to linked table having custom summary' ); - RETURN NEXT throws_ok( - format( - $s$SELECT msar.get_record_from_table( - tab_id => %1$s, - rec_id => 4, - return_record_summaries => true, - table_record_summary_templates => jsonb_build_object( - %1$s, - '[[4], " ", [5], "%% - (", [3, 3], " / ", [3, 2, 2], ")"]'::jsonb - ) - );$s$, - '"Students"'::regclass::oid - ), - '42501', - 'permission denied for table Teachers', - 'Record summary throws permission error when linked table missing privileges -- TODO' - ); SET ROLE NONE; CREATE ROLE intern_no_access;