From 47806e7f69935caaa86f40e87cf215cb90aaf9a3 Mon Sep 17 00:00:00 2001 From: Koval Dmitry Date: Thu, 22 Dec 2022 05:10:34 +0300 Subject: [PATCH 1/3] [PGPRO-7585] Fixes for v16 due to vanilla changes Tags: pg_pathman Caused by: - ad86d159b6: Add 'missing_ok' argument to build_attrmap_by_name - a61b1f7482: Rework query relation permission checking - b5d6382496: Provide per-table permissions for vacuum and analyze --- expected/pathman_permissions_1.out | 263 +++++++++++++++++++++++++++++ src/include/partition_filter.h | 8 +- src/partition_filter.c | 68 +++++++- src/pg_pathman.c | 7 + src/pl_funcs.c | 5 +- src/planner_tree_modification.c | 27 +++ src/utility_stmt_hooking.c | 47 +++++- 7 files changed, 410 insertions(+), 15 deletions(-) create mode 100644 expected/pathman_permissions_1.out diff --git a/expected/pathman_permissions_1.out b/expected/pathman_permissions_1.out new file mode 100644 index 00000000..c7e04210 --- /dev/null +++ b/expected/pathman_permissions_1.out @@ -0,0 +1,263 @@ +\set VERBOSITY terse +SET search_path = 'public'; +CREATE EXTENSION pg_pathman; +CREATE SCHEMA permissions; +CREATE ROLE user1 LOGIN; +CREATE ROLE user2 LOGIN; +GRANT USAGE, CREATE ON SCHEMA permissions TO user1; +GRANT USAGE, CREATE ON SCHEMA permissions TO user2; +/* Switch to #1 */ +SET ROLE user1; +CREATE TABLE permissions.user1_table(id serial, a int); +INSERT INTO permissions.user1_table SELECT g, g FROM generate_series(1, 20) as g; +/* Should fail (can't SELECT) */ +SET ROLE user2; +DO $$ +BEGIN + SELECT create_range_partitions('permissions.user1_table', 'id', 1, 10, 2); +EXCEPTION + WHEN insufficient_privilege THEN + RAISE NOTICE 'Insufficient priviliges'; +END$$; +NOTICE: Insufficient priviliges +/* Grant SELECT to user2 */ +SET ROLE user1; +GRANT SELECT ON permissions.user1_table TO user2; +/* Should fail (don't own parent) */ +SET ROLE user2; +DO $$ +BEGIN + SELECT create_range_partitions('permissions.user1_table', 'id', 1, 10, 2); +EXCEPTION + WHEN insufficient_privilege THEN + RAISE NOTICE 'Insufficient priviliges'; +END$$; +NOTICE: Insufficient priviliges +/* Should be ok */ +SET ROLE user1; +SELECT create_range_partitions('permissions.user1_table', 'id', 1, 10, 2); + create_range_partitions +------------------------- + 2 +(1 row) + +/* Should be able to see */ +SET ROLE user2; +SELECT * FROM pathman_config; + partrel | expr | parttype | range_interval +-------------------------+------+----------+---------------- + permissions.user1_table | id | 2 | 10 +(1 row) + +SELECT * FROM pathman_config_params; + partrel | enable_parent | auto | init_callback | spawn_using_bgw +-------------------------+---------------+------+---------------+----------------- + permissions.user1_table | f | t | | f +(1 row) + +/* Should fail */ +SET ROLE user2; +SELECT set_enable_parent('permissions.user1_table', true); +WARNING: only the owner or superuser can change partitioning configuration of table "user1_table" +ERROR: new row violates row-level security policy for table "pathman_config_params" +SELECT set_auto('permissions.user1_table', false); +WARNING: only the owner or superuser can change partitioning configuration of table "user1_table" +ERROR: new row violates row-level security policy for table "pathman_config_params" +/* Should fail */ +SET ROLE user2; +DELETE FROM pathman_config +WHERE partrel = 'permissions.user1_table'::regclass; +WARNING: only the owner or superuser can change partitioning configuration of table "user1_table" +/* No rights to insert, should fail */ +SET ROLE user2; +DO $$ +BEGIN + INSERT INTO permissions.user1_table (id, a) VALUES (35, 0); +EXCEPTION + WHEN insufficient_privilege THEN + RAISE NOTICE 'Insufficient priviliges'; +END$$; +NOTICE: Insufficient priviliges +/* No rights to create partitions (need INSERT privilege) */ +SET ROLE user2; +SELECT prepend_range_partition('permissions.user1_table'); +ERROR: permission denied for parent relation "user1_table" +/* Allow user2 to create partitions */ +SET ROLE user1; +GRANT INSERT ON permissions.user1_table TO user2; +GRANT UPDATE(a) ON permissions.user1_table TO user2; /* per-column ACL */ +/* Should be able to prepend a partition */ +SET ROLE user2; +SELECT prepend_range_partition('permissions.user1_table'); + prepend_range_partition +--------------------------- + permissions.user1_table_4 +(1 row) + +SELECT attname, attacl FROM pg_attribute +WHERE attrelid = (SELECT "partition" FROM pathman_partition_list + WHERE parent = 'permissions.user1_table'::REGCLASS + ORDER BY range_min::int ASC /* prepend */ + LIMIT 1) +ORDER BY attname; /* check ACL for each column */ + attname | attacl +----------+----------------- + a | {user2=w/user1} + cmax | + cmin | + ctid | + id | + tableoid | + xmax | + xmin | +(8 rows) + +/* Have rights, should be ok (parent's ACL is shared by new children) */ +SET ROLE user2; +INSERT INTO permissions.user1_table (id, a) VALUES (35, 0) RETURNING *; + id | a +----+--- + 35 | 0 +(1 row) + +SELECT relname, relacl FROM pg_class +WHERE oid = ANY (SELECT "partition" FROM pathman_partition_list + WHERE parent = 'permissions.user1_table'::REGCLASS + ORDER BY range_max::int DESC /* append */ + LIMIT 3) +ORDER BY relname; /* we also check ACL for "user1_table_2" */ + relname | relacl +---------------+---------------------------------------- + user1_table_2 | {user1=arwdDxtvz/user1,user2=r/user1} + user1_table_5 | {user1=arwdDxtvz/user1,user2=ar/user1} + user1_table_6 | {user1=arwdDxtvz/user1,user2=ar/user1} +(3 rows) + +/* Try to drop partition, should fail */ +DO $$ +BEGIN + SELECT drop_range_partition('permissions.user1_table_4'); +EXCEPTION + WHEN insufficient_privilege THEN + RAISE NOTICE 'Insufficient priviliges'; +END$$; +NOTICE: Insufficient priviliges +/* Disable automatic partition creation */ +SET ROLE user1; +SELECT set_auto('permissions.user1_table', false); + set_auto +---------- + +(1 row) + +/* Partition creation, should fail */ +SET ROLE user2; +INSERT INTO permissions.user1_table (id, a) VALUES (55, 0) RETURNING *; +ERROR: no suitable partition for key '55' +/* Finally drop partitions */ +SET ROLE user1; +SELECT drop_partitions('permissions.user1_table'); +NOTICE: 10 rows copied from permissions.user1_table_1 +NOTICE: 10 rows copied from permissions.user1_table_2 +NOTICE: 0 rows copied from permissions.user1_table_4 +NOTICE: 0 rows copied from permissions.user1_table_5 +NOTICE: 1 rows copied from permissions.user1_table_6 + drop_partitions +----------------- + 5 +(1 row) + +/* Switch to #2 */ +SET ROLE user2; +/* Test ddl event trigger */ +CREATE TABLE permissions.user2_table(id serial); +SELECT create_hash_partitions('permissions.user2_table', 'id', 3); + create_hash_partitions +------------------------ + 3 +(1 row) + +INSERT INTO permissions.user2_table SELECT generate_series(1, 30); +SELECT drop_partitions('permissions.user2_table'); +NOTICE: 9 rows copied from permissions.user2_table_0 +NOTICE: 11 rows copied from permissions.user2_table_1 +NOTICE: 10 rows copied from permissions.user2_table_2 + drop_partitions +----------------- + 3 +(1 row) + +/* Switch to #1 */ +SET ROLE user1; +CREATE TABLE permissions.dropped_column(a int, val int not null, b int, c int); +INSERT INTO permissions.dropped_column SELECT i,i,i,i FROM generate_series(1, 30) i; +GRANT SELECT(val), INSERT(val) ON permissions.dropped_column TO user2; +SELECT create_range_partitions('permissions.dropped_column', 'val', 1, 10); + create_range_partitions +------------------------- + 3 +(1 row) + +SELECT attrelid::regclass, attname, attacl FROM pg_attribute +WHERE attrelid = ANY (SELECT "partition" FROM pathman_partition_list + WHERE parent = 'permissions.dropped_column'::REGCLASS) + AND attacl IS NOT NULL +ORDER BY attrelid::regclass::text; /* check ACL for each column */ + attrelid | attname | attacl +------------------------------+---------+------------------ + permissions.dropped_column_1 | val | {user2=ar/user1} + permissions.dropped_column_2 | val | {user2=ar/user1} + permissions.dropped_column_3 | val | {user2=ar/user1} +(3 rows) + +ALTER TABLE permissions.dropped_column DROP COLUMN a; /* DROP "a" */ +SELECT append_range_partition('permissions.dropped_column'); + append_range_partition +------------------------------ + permissions.dropped_column_4 +(1 row) + +SELECT attrelid::regclass, attname, attacl FROM pg_attribute +WHERE attrelid = ANY (SELECT "partition" FROM pathman_partition_list + WHERE parent = 'permissions.dropped_column'::REGCLASS) + AND attacl IS NOT NULL +ORDER BY attrelid::regclass::text; /* check ACL for each column (+1 partition) */ + attrelid | attname | attacl +------------------------------+---------+------------------ + permissions.dropped_column_1 | val | {user2=ar/user1} + permissions.dropped_column_2 | val | {user2=ar/user1} + permissions.dropped_column_3 | val | {user2=ar/user1} + permissions.dropped_column_4 | val | {user2=ar/user1} +(4 rows) + +ALTER TABLE permissions.dropped_column DROP COLUMN b; /* DROP "b" */ +SELECT append_range_partition('permissions.dropped_column'); + append_range_partition +------------------------------ + permissions.dropped_column_5 +(1 row) + +SELECT attrelid::regclass, attname, attacl FROM pg_attribute +WHERE attrelid = ANY (SELECT "partition" FROM pathman_partition_list + WHERE parent = 'permissions.dropped_column'::REGCLASS) + AND attacl IS NOT NULL +ORDER BY attrelid::regclass::text; /* check ACL for each column (+1 partition) */ + attrelid | attname | attacl +------------------------------+---------+------------------ + permissions.dropped_column_1 | val | {user2=ar/user1} + permissions.dropped_column_2 | val | {user2=ar/user1} + permissions.dropped_column_3 | val | {user2=ar/user1} + permissions.dropped_column_4 | val | {user2=ar/user1} + permissions.dropped_column_5 | val | {user2=ar/user1} +(5 rows) + +DROP TABLE permissions.dropped_column CASCADE; +NOTICE: drop cascades to 6 other objects +/* Finally reset user */ +RESET ROLE; +DROP OWNED BY user1; +DROP OWNED BY user2; +DROP USER user1; +DROP USER user2; +DROP SCHEMA permissions; +DROP EXTENSION pg_pathman; diff --git a/src/include/partition_filter.h b/src/include/partition_filter.h index 0c912abe..d3c2c482 100644 --- a/src/include/partition_filter.h +++ b/src/include/partition_filter.h @@ -101,6 +101,9 @@ struct ResultPartsStorage PartRelationInfo *prel; ExprState *prel_expr_state; ExprContext *prel_econtext; +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + ResultRelInfo *init_rri; /* first initialized ResultRelInfo */ +#endif }; typedef struct @@ -167,7 +170,7 @@ void init_result_parts_storage(ResultPartsStorage *parts_storage, void fini_result_parts_storage(ResultPartsStorage *parts_storage); /* Find ResultRelInfo holder in storage */ -ResultRelInfoHolder * scan_result_parts_storage(ResultPartsStorage *storage, Oid partid); +ResultRelInfoHolder * scan_result_parts_storage(EState *estate, ResultPartsStorage *storage, Oid partid); /* Refresh PartRelationInfo in storage */ PartRelationInfo * refresh_result_parts_storage(ResultPartsStorage *parts_storage, Oid partid); @@ -186,7 +189,8 @@ Oid * find_partitions_for_value(Datum value, Oid value_type, const PartRelationInfo *prel, int *nparts); -ResultRelInfoHolder *select_partition_for_insert(ResultPartsStorage *parts_storage, +ResultRelInfoHolder *select_partition_for_insert(EState *estate, + ResultPartsStorage *parts_storage, TupleTableSlot *slot); Plan * make_partition_filter(Plan *subplan, diff --git a/src/partition_filter.c b/src/partition_filter.c index 3a72a70d..a267c702 100644 --- a/src/partition_filter.c +++ b/src/partition_filter.c @@ -27,6 +27,9 @@ #include "foreign/fdwapi.h" #include "foreign/foreign.h" #include "nodes/nodeFuncs.h" +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ +#include "parser/parse_relation.h" +#endif #include "rewrite/rewriteManip.h" #include "utils/guc.h" #include "utils/memutils.h" @@ -257,7 +260,8 @@ fini_result_parts_storage(ResultPartsStorage *parts_storage) /* Find a ResultRelInfo for the partition using ResultPartsStorage */ ResultRelInfoHolder * -scan_result_parts_storage(ResultPartsStorage *parts_storage, Oid partid) +scan_result_parts_storage(EState *estate, ResultPartsStorage *parts_storage, + Oid partid) { #define CopyToResultRelInfo(field_name) \ ( child_result_rel_info->field_name = parts_storage->base_rri->field_name ) @@ -280,6 +284,12 @@ scan_result_parts_storage(ResultPartsStorage *parts_storage, Oid partid) ResultRelInfo *child_result_rel_info; List *translated_vars; MemoryContext old_mcxt; +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + RTEPermissionInfo *parent_perminfo, + *child_perminfo; + /* ResultRelInfo of partitioned table. */ + RangeTblEntry *init_rte; +#endif /* Lock partition and check if it exists */ LockRelationOid(partid, parts_storage->head_open_lock_mode); @@ -306,15 +316,41 @@ scan_result_parts_storage(ResultPartsStorage *parts_storage, Oid partid) /* Open child relation and check if it is a valid target */ child_rel = heap_open_compat(partid, NoLock); - /* Build Var translation list for 'inserted_cols' */ - make_inh_translation_list(base_rel, child_rel, 0, &translated_vars, NULL); - /* Create RangeTblEntry for partition */ child_rte = makeNode(RangeTblEntry); child_rte->rtekind = RTE_RELATION; child_rte->relid = partid; child_rte->relkind = child_rel->rd_rel->relkind; child_rte->eref = parent_rte->eref; +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + /* Build Var translation list for 'inserted_cols' */ + make_inh_translation_list(parts_storage->init_rri->ri_RelationDesc, + child_rel, 0, &translated_vars, NULL); + + /* + * Need to use ResultRelInfo of partitioned table 'init_rri' because + * 'base_rri' can be ResultRelInfo of partition without any + * ResultRelInfo, see expand_single_inheritance_child(). + */ + init_rte = rt_fetch(parts_storage->init_rri->ri_RangeTableIndex, + parts_storage->estate->es_range_table); + parent_perminfo = getRTEPermissionInfo(estate->es_rteperminfos, init_rte); + + child_rte->perminfoindex = 0; /* expected by addRTEPermissionInfo() */ + child_perminfo = addRTEPermissionInfo(&estate->es_rteperminfos, child_rte); + child_perminfo->requiredPerms = parent_perminfo->requiredPerms; + child_perminfo->checkAsUser = parent_perminfo->checkAsUser; + child_perminfo->insertedCols = translate_col_privs(parent_perminfo->insertedCols, + translated_vars); + child_perminfo->updatedCols = translate_col_privs(parent_perminfo->updatedCols, + translated_vars); + + /* Check permissions for partition */ + ExecCheckPermissions(list_make1(child_rte), list_make1(child_perminfo), true); +#else + /* Build Var translation list for 'inserted_cols' */ + make_inh_translation_list(base_rel, child_rel, 0, &translated_vars, NULL); + child_rte->requiredPerms = parent_rte->requiredPerms; child_rte->checkAsUser = parent_rte->checkAsUser; child_rte->insertedCols = translate_col_privs(parent_rte->insertedCols, @@ -324,6 +360,7 @@ scan_result_parts_storage(ResultPartsStorage *parts_storage, Oid partid) /* Check permissions for partition */ ExecCheckRTPerms(list_make1(child_rte), true); +#endif /* Append RangeTblEntry to estate->es_range_table */ child_rte_idx = append_rte_to_estate(parts_storage->estate, child_rte, child_rel); @@ -498,7 +535,9 @@ build_part_tuple_map_child(Relation child_rel) child_tupdesc2->tdtypeid = InvalidOid; /* Generate tuple transformation map */ -#if PG_VERSION_NUM >= 130000 +#if PG_VERSION_NUM >= 160000 /* for commit ad86d159b6ab */ + attrMap = build_attrmap_by_name(child_tupdesc1, child_tupdesc2, false); +#elif PG_VERSION_NUM >= 130000 attrMap = build_attrmap_by_name(child_tupdesc1, child_tupdesc2); #else attrMap = convert_tuples_by_name_map(child_tupdesc1, child_tupdesc2, @@ -586,7 +625,8 @@ find_partitions_for_value(Datum value, Oid value_type, * Smart wrapper for scan_result_parts_storage(). */ ResultRelInfoHolder * -select_partition_for_insert(ResultPartsStorage *parts_storage, +select_partition_for_insert(EState *estate, + ResultPartsStorage *parts_storage, TupleTableSlot *slot) { PartRelationInfo *prel = parts_storage->prel; @@ -637,7 +677,7 @@ select_partition_for_insert(ResultPartsStorage *parts_storage, else partition_relid = parts[0]; /* Get ResultRelationInfo holder for the selected partition */ - result = scan_result_parts_storage(parts_storage, partition_relid); + result = scan_result_parts_storage(estate, parts_storage, partition_relid); /* Somebody has dropped or created partitions */ if ((nparts == 0 || result == NULL) && !PrelIsFresh(prel)) @@ -837,6 +877,10 @@ partition_filter_begin(CustomScanState *node, EState *estate, int eflags) state->on_conflict_action != ONCONFLICT_NONE, RPS_RRI_CB(prepare_rri_for_insert, state), RPS_RRI_CB(NULL, NULL)); +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + /* ResultRelInfo of partitioned table. */ + state->result_parts.init_rri = current_rri; +#endif } #if PG_VERSION_NUM >= 140000 @@ -906,7 +950,7 @@ partition_filter_exec(CustomScanState *node) old_mcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); /* Search for a matching partition */ - rri_holder = select_partition_for_insert(&state->result_parts, slot); + rri_holder = select_partition_for_insert(estate, &state->result_parts, slot); /* Switch back and clean up per-tuple context */ MemoryContextSwitchTo(old_mcxt); @@ -1223,6 +1267,14 @@ prepare_rri_fdw_for_insert(ResultRelInfoHolder *rri_holder, query.targetList = NIL; query.returningList = NIL; +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + /* + * Copy the RTEPermissionInfos into query as well, so that + * add_rte_to_flat_rtable() will work correctly. + */ + query.rteperminfos = estate->es_rteperminfos; +#endif + /* Generate 'query.targetList' using 'tupdesc' */ target_attr = 1; for (i = 0; i < tupdesc->natts; i++) diff --git a/src/pg_pathman.c b/src/pg_pathman.c index 34600249..2e8b1d7e 100644 --- a/src/pg_pathman.c +++ b/src/pg_pathman.c @@ -551,7 +551,12 @@ append_child_relation(PlannerInfo *root, #endif child_rte->relid = child_oid; child_rte->relkind = child_relation->rd_rel->relkind; +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + /* No permission checking for the child RTE */ + child_rte->perminfoindex = 0; +#else child_rte->requiredPerms = 0; /* perform all checks on parent */ +#endif child_rte->inh = false; /* Add 'child_rte' to rtable and 'root->simple_rte_array' */ @@ -676,6 +681,7 @@ append_child_relation(PlannerInfo *root, } +#if PG_VERSION_NUM < 160000 /* for commit a61b1f74823c */ /* Translate column privileges for this child */ if (parent_rte->relid != child_oid) { @@ -694,6 +700,7 @@ append_child_relation(PlannerInfo *root, child_rte->updatedCols = bms_copy(parent_rte->updatedCols); } #endif +#endif /* PG_VERSION_NUM < 160000 */ /* Here and below we assume that parent RelOptInfo exists */ Assert(parent_rel); diff --git a/src/pl_funcs.c b/src/pl_funcs.c index 809884c2..542f99ae 100644 --- a/src/pl_funcs.c +++ b/src/pl_funcs.c @@ -725,7 +725,10 @@ is_tuple_convertible(PG_FUNCTION_ARGS) rel2 = heap_open_compat(PG_GETARG_OID(1), AccessShareLock); /* Try to build a conversion map */ -#if PG_VERSION_NUM >= 130000 +#if PG_VERSION_NUM >= 160000 /* for commit ad86d159b6ab */ + map = build_attrmap_by_name(RelationGetDescr(rel1), + RelationGetDescr(rel2), false); +#elif PG_VERSION_NUM >= 130000 map = build_attrmap_by_name(RelationGetDescr(rel1), RelationGetDescr(rel2)); #else diff --git a/src/planner_tree_modification.c b/src/planner_tree_modification.c index 027fd4e1..d9d64cfd 100644 --- a/src/planner_tree_modification.c +++ b/src/planner_tree_modification.c @@ -27,6 +27,9 @@ #include "foreign/fdwapi.h" #include "miscadmin.h" #include "optimizer/clauses.h" +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ +#include "parser/parse_relation.h" +#endif #include "storage/lmgr.h" #include "utils/syscache.h" @@ -578,6 +581,10 @@ handle_modification_query(Query *parse, transform_query_cxt *context) List *translated_vars; adjust_appendrel_varnos_cxt aav_cxt; +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + RTEPermissionInfo *parent_perminfo, + *child_perminfo; +#endif /* Lock 'child' table */ LockRelationOid(child, lockmode); @@ -598,10 +605,24 @@ handle_modification_query(Query *parse, transform_query_cxt *context) return; /* nothing to do here */ } +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + parent_perminfo = getRTEPermissionInfo(parse->rteperminfos, rte); +#endif /* Update RTE's relid and relkind (for FDW) */ rte->relid = child; rte->relkind = child_relkind; +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + /* Copy parent RTEPermissionInfo. */ + rte->perminfoindex = 0; /* expected by addRTEPermissionInfo() */ + child_perminfo = addRTEPermissionInfo(&parse->rteperminfos, rte); + memcpy(child_perminfo, parent_perminfo, sizeof(RTEPermissionInfo)); + + /* Correct RTEPermissionInfo for child. */ + child_perminfo->relid = child; + child_perminfo->inh = false; +#endif + /* HACK: unset the 'inh' flag (no children) */ rte->inh = false; @@ -622,10 +643,16 @@ handle_modification_query(Query *parse, transform_query_cxt *context) aav_cxt.translated_vars = translated_vars; adjust_appendrel_varnos((Node *) parse, &aav_cxt); +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + child_perminfo->selectedCols = translate_col_privs(parent_perminfo->selectedCols, translated_vars); + child_perminfo->insertedCols = translate_col_privs(parent_perminfo->insertedCols, translated_vars); + child_perminfo->updatedCols = translate_col_privs(parent_perminfo->updatedCols, translated_vars); +#else /* Translate column privileges for this child */ rte->selectedCols = translate_col_privs(rte->selectedCols, translated_vars); rte->insertedCols = translate_col_privs(rte->insertedCols, translated_vars); rte->updatedCols = translate_col_privs(rte->updatedCols, translated_vars); +#endif } /* Close relations (should remain locked, though) */ diff --git a/src/utility_stmt_hooking.c b/src/utility_stmt_hooking.c index 35786092..d1d9010c 100644 --- a/src/utility_stmt_hooking.c +++ b/src/utility_stmt_hooking.c @@ -26,12 +26,18 @@ #include "access/xact.h" #include "catalog/namespace.h" #include "commands/copy.h" +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ +#include "commands/copyfrom_internal.h" +#endif #include "commands/defrem.h" #include "commands/trigger.h" #include "commands/tablecmds.h" #include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ +#include "parser/parse_relation.h" +#endif #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/memutils.h" @@ -414,6 +420,9 @@ PathmanDoCopy(const CopyStmt *stmt, "psql's \\copy command also works for anyone."))); } + pstate = make_parsestate(NULL); + pstate->p_sourcetext = queryString; + /* Check that we have a relation */ if (stmt->relation) { @@ -422,6 +431,9 @@ PathmanDoCopy(const CopyStmt *stmt, List *attnums; ListCell *cur; RangeTblEntry *rte; +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + RTEPermissionInfo *perminfo; +#endif Assert(!stmt->query); @@ -432,11 +444,30 @@ PathmanDoCopy(const CopyStmt *stmt, rte->rtekind = RTE_RELATION; rte->relid = RelationGetRelid(rel); rte->relkind = rel->rd_rel->relkind; +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + pstate->p_rtable = lappend(pstate->p_rtable, rte); + perminfo = addRTEPermissionInfo(&pstate->p_rteperminfos, rte); + perminfo->requiredPerms = required_access; +#else rte->requiredPerms = required_access; +#endif range_table = list_make1(rte); tupDesc = RelationGetDescr(rel); attnums = PathmanCopyGetAttnums(tupDesc, rel, stmt->attlist); +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + foreach(cur, attnums) + { + int attno; + Bitmapset **bms; + + attno = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber; + bms = is_from ? &perminfo->insertedCols : &perminfo->selectedCols; + + *bms = bms_add_member(*bms, attno); + } + ExecCheckPermissions(pstate->p_rtable, list_make1(perminfo), true); +#else foreach(cur, attnums) { int attnum = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber; @@ -447,6 +478,7 @@ PathmanDoCopy(const CopyStmt *stmt, rte->selectedCols = bms_add_member(rte->selectedCols, attnum); } ExecCheckRTPerms(range_table, true); +#endif /* Disable COPY FROM if table has RLS */ if (is_from && check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) @@ -470,9 +502,6 @@ PathmanDoCopy(const CopyStmt *stmt, /* This should never happen (see is_pathman_related_copy()) */ else elog(ERROR, "error in function " CppAsString(PathmanDoCopy)); - pstate = make_parsestate(NULL); - pstate->p_sourcetext = queryString; - if (is_from) { /* check read-only transaction and parallel mode */ @@ -567,6 +596,16 @@ PathmanCopyFrom( RPS_DEFAULT_SPECULATIVE, RPS_RRI_CB(prepare_rri_for_copy, cstate), RPS_RRI_CB(finish_rri_for_copy, NULL)); +#if PG_VERSION_NUM >= 160000 /* for commit a61b1f74823c */ + /* ResultRelInfo of partitioned table. */ + parts_storage.init_rri = parent_rri; + + /* + * Copy the RTEPermissionInfos into estate as well, so that + * scan_result_parts_storage() et al will work correctly. + */ + estate->es_rteperminfos = cstate->rteperminfos; +#endif /* Set up a tuple slot too */ myslot = ExecInitExtraTupleSlotCompat(estate, NULL, &TTSOpsHeapTuple); @@ -629,7 +668,7 @@ PathmanCopyFrom( #endif /* Search for a matching partition */ - rri_holder = select_partition_for_insert(&parts_storage, slot); + rri_holder = select_partition_for_insert(estate, &parts_storage, slot); child_rri = rri_holder->result_rel_info; /* Magic: replace parent's ResultRelInfo with ours */ From bb9f6e49a7643b77126fb2575a96024bba0ae326 Mon Sep 17 00:00:00 2001 From: Marina Polyakova Date: Tue, 10 Jan 2023 19:13:48 +0300 Subject: [PATCH 2/3] Convert the reg* input functions to report (most) errors softly. See the commit 858e776c84f48841e7e16fba7b690b76e54f3675 (Convert the reg* input functions to report (most) errors softly.) in PostgreSQL 16. The function qualified_relnames_to_rangevars is used in the functions create_hash_partitions_internal and create_range_partitions_internal. It looks like these functions should not skip partition names (e.g. in the functions create_hash_partitions and create_range_partitions respectively).. --- src/include/compat/pg_compat.h | 11 +++++++++++ src/utils.c | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/include/compat/pg_compat.h b/src/include/compat/pg_compat.h index 80a76d60..4ae249e6 100644 --- a/src/include/compat/pg_compat.h +++ b/src/include/compat/pg_compat.h @@ -1084,6 +1084,17 @@ extern AttrNumber *convert_tuples_by_name_map(TupleDesc indesc, expression_tree_mutator((node), (mutator), (context)) #endif +/* + * stringToQualifiedNameList + */ +#if PG_VERSION_NUM >= 160000 +#define stringToQualifiedNameListCompat(string) \ + stringToQualifiedNameList((string), NULL) +#else +#define stringToQualifiedNameListCompat(string) \ + stringToQualifiedNameList((string)) +#endif + /* * ------------- * Common code diff --git a/src/utils.c b/src/utils.c index 15552f56..6ebfb8a8 100644 --- a/src/utils.c +++ b/src/utils.c @@ -518,7 +518,7 @@ qualified_relnames_to_rangevars(char **relnames, size_t nrelnames) rangevars = palloc(sizeof(RangeVar *) * nrelnames); for (i = 0; i < nrelnames; i++) { - List *nl = stringToQualifiedNameList(relnames[i]); + List *nl = stringToQualifiedNameListCompat(relnames[i]); rangevars[i] = makeRangeVarFromNameList(nl); } From 2d49e88e1cb6c3df338ba82d733e0b2e896d0e15 Mon Sep 17 00:00:00 2001 From: Marina Polyakova Date: Tue, 10 Jan 2023 19:19:57 +0300 Subject: [PATCH 3/3] Add grantable MAINTAIN privilege and pg_maintain role. See the commit 60684dd834a222fefedd49b19d1f0a6189c1632e (Add grantable MAINTAIN privilege and pg_maintain role.) in PostgreSQL 16. Since pathman_permissions_1.out is already in use for PostgreSQL 16+ (see the commit 47806e7f69935caaa86f40e87cf215cb90aaf9a3 [PGPRO-7585] Fixes for v16 due to vanilla changes), do not create pathman_permissions_2.out. --- expected/pathman_permissions_1.out | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/expected/pathman_permissions_1.out b/expected/pathman_permissions_1.out index c7e04210..a50aa524 100644 --- a/expected/pathman_permissions_1.out +++ b/expected/pathman_permissions_1.out @@ -126,11 +126,11 @@ WHERE oid = ANY (SELECT "partition" FROM pathman_partition_list ORDER BY range_max::int DESC /* append */ LIMIT 3) ORDER BY relname; /* we also check ACL for "user1_table_2" */ - relname | relacl ----------------+---------------------------------------- - user1_table_2 | {user1=arwdDxtvz/user1,user2=r/user1} - user1_table_5 | {user1=arwdDxtvz/user1,user2=ar/user1} - user1_table_6 | {user1=arwdDxtvz/user1,user2=ar/user1} + relname | relacl +---------------+--------------------------------------- + user1_table_2 | {user1=arwdDxtm/user1,user2=r/user1} + user1_table_5 | {user1=arwdDxtm/user1,user2=ar/user1} + user1_table_6 | {user1=arwdDxtm/user1,user2=ar/user1} (3 rows) /* Try to drop partition, should fail */