From e5cf2a6bb6e47254d8d87bce4f3db71959c7a5c0 Mon Sep 17 00:00:00 2001 From: liu qiren <13559537330@163.com> Date: Sat, 2 May 2026 08:04:17 +0800 Subject: [PATCH 1/4] Feature: reject partition fullscan as gpcontrib extension Add a planner_hook-based extension under gpcontrib/ that rejects queries on partitioned tables when no effective partition pruning occurs. This keeps all core optimizer files untouched. The extension uses three complementary detection strategies: 1. PartitionPruneInfo check: for Planner Append nodes with pruning conditions and ORCA PartitionSelector nodes. 2. Append heuristic: for Planner Append/MergeAppend without PartitionPruneInfo (no WHERE, WHERE 1=1, non-partition-key WHERE), detected by cross-referencing apprelids with partitioned-table RTEs. 3. DynamicScan check: for ORCA DynamicSeqScan and similar nodes whose part_prune_info is never set by ORCA, detected by comparing partOids count against catalog partition count. New GUC parameters (registered via DefineCustomXxxVariable): - reject_partition_fullscan (bool, default on) - partition_fullscan_threshold (int, default 0) Exemptions: enable_partition_pruning=off, single-partition tables, runtime pruning steps (prepared statements), JOIN dynamic pruning. --- gpcontrib/Makefile | 6 +- gpcontrib/reject_partition_fullscan/Makefile | 18 + .../reject_partition_fullscan--1.0.sql | 8 + .../reject_partition_fullscan.c | 470 ++++++++++++++++++ .../reject_partition_fullscan.control | 4 + .../sql/partition_fullscan_reject.sql | 113 +++++ 6 files changed, 617 insertions(+), 2 deletions(-) create mode 100644 gpcontrib/reject_partition_fullscan/Makefile create mode 100644 gpcontrib/reject_partition_fullscan/reject_partition_fullscan--1.0.sql create mode 100644 gpcontrib/reject_partition_fullscan/reject_partition_fullscan.c create mode 100644 gpcontrib/reject_partition_fullscan/reject_partition_fullscan.control create mode 100644 gpcontrib/reject_partition_fullscan/sql/partition_fullscan_reject.sql diff --git a/gpcontrib/Makefile b/gpcontrib/Makefile index 2969194cfac..cce33927ad9 100644 --- a/gpcontrib/Makefile +++ b/gpcontrib/Makefile @@ -22,7 +22,8 @@ ifeq "$(enable_debug_extensions)" "yes" gp_legacy_string_agg \ gp_replica_check \ gp_toolkit \ - pg_hint_plan + pg_hint_plan \ + reject_partition_fullscan else recurse_targets = gp_sparse_vector \ gp_distribution_policy \ @@ -30,7 +31,8 @@ else gp_legacy_string_agg \ gp_exttable_fdw \ gp_toolkit \ - pg_hint_plan + pg_hint_plan \ + reject_partition_fullscan endif ifeq "$(with_diskquota)" "yes" diff --git a/gpcontrib/reject_partition_fullscan/Makefile b/gpcontrib/reject_partition_fullscan/Makefile new file mode 100644 index 00000000000..3e762c4c1f5 --- /dev/null +++ b/gpcontrib/reject_partition_fullscan/Makefile @@ -0,0 +1,18 @@ +MODULE_big = reject_partition_fullscan +OBJS = reject_partition_fullscan.o + +EXTENSION = reject_partition_fullscan +DATA = reject_partition_fullscan--1.0.sql + +REGRESS = partition_fullscan_reject + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = gpcontrib/reject_partition_fullscan +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/gpcontrib/reject_partition_fullscan/reject_partition_fullscan--1.0.sql b/gpcontrib/reject_partition_fullscan/reject_partition_fullscan--1.0.sql new file mode 100644 index 00000000000..91ce4676f67 --- /dev/null +++ b/gpcontrib/reject_partition_fullscan/reject_partition_fullscan--1.0.sql @@ -0,0 +1,8 @@ +/* gpcontrib/reject_partition_fullscan/reject_partition_fullscan--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION reject_partition_fullscan" to load this file. \quit + +-- Extension is loaded via shared_preload_libraries or LOAD command. +-- No SQL objects needed; the planner hook and GUCs are registered +-- automatically in _PG_init(). diff --git a/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.c b/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.c new file mode 100644 index 00000000000..332a300ef1a --- /dev/null +++ b/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.c @@ -0,0 +1,470 @@ +/*------------------------------------------------------------------------- + * + * reject_partition_fullscan.c + * Extension to reject queries that scan all partitions of a + * partitioned table without effective partition pruning. + * + * This extension installs a planner_hook that wraps the standard planner + * (including ORCA). After the planner produces a PlannedStmt, the hook + * walks the plan tree looking for partition scan nodes that indicate no + * effective pruning occurred. + * + * Detection strategy (three complementary checks): + * + * 1) Nodes with PartitionPruneInfo (Planner Append with WHERE, or + * PartitionSelector in ORCA JOIN path): compare present_parts vs + * nparts. Exempt nodes with initial/exec pruning steps (runtime + * pruning capable). + * + * 2) Planner Append/MergeAppend without PartitionPruneInfo (no WHERE, + * WHERE 1=1, WHERE on non-partition-key): the Planner does not + * generate PartitionPruneInfo when there are no useful pruning quals. + * We detect these by checking if apprelids references a partitioned + * table RTE (relkind='p', inh=true). + * + * 3) ORCA DynamicSeqScan (and similar Dynamic nodes): ORCA never sets + * part_prune_info on Dynamic scan nodes. We detect full scans by + * comparing list_length(partOids) against the total partition count + * from catalog. Nodes with join_prune_paramids are skipped (JOIN + * dynamic pruning). + * + * GUC parameters (registered via DefineCustomXxxVariable): + * reject_partition_fullscan (bool, default true) + * partition_fullscan_threshold (int, default 0) + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "catalog/pg_class.h" +#include "catalog/pg_inherits.h" +#include "cdb/cdbllize.h" +#include "nodes/bitmapset.h" +#include "nodes/nodeFuncs.h" +#include "nodes/plannodes.h" +#include "optimizer/cost.h" +#include "optimizer/planner.h" +#include "optimizer/walkers.h" +#include "parser/parsetree.h" +#include "utils/guc.h" +#include "utils/lsyscache.h" + +PG_MODULE_MAGIC; + +/* GUC variables */ +static bool reject_fullscan_enabled = true; +static int fullscan_threshold = 0; + +/* Saved previous hook */ +static planner_hook_type prev_planner_hook = NULL; + +/* Forward declarations */ +void _PG_init(void); +void _PG_fini(void); + +static PlannedStmt *rpf_planner_hook(Query *parse, + const char *query_string, + int cursorOptions, + ParamListInfo boundParams, + OptimizerOptions *optimizer_options); +static void check_partition_fullscan(PlannedStmt *stmt); + +/* ---------------------------------------------------------------- + * Utility: raise the rejection ERROR + * ---------------------------------------------------------------- + */ +static void +reject_fullscan(const char *nspname, const char *relname, int nparts) +{ + ereport(ERROR, + (errcode(ERRCODE_STATEMENT_TOO_COMPLEX), + errmsg("partitioned table \"%s.%s\" full partition " + "scan is not allowed, %d partitions would " + "be scanned", + nspname, relname, nparts), + errhint("Add a WHERE clause on the partition key " + "to enable partition pruning."))); +} + +/* ---------------------------------------------------------------- + * Check 1: Nodes with PartitionPruneInfo + * Used by: Planner Append (with pruning quals), PartitionSelector + * ---------------------------------------------------------------- + */ +static PartitionPruneInfo * +get_part_prune_info(Plan *plan) +{ + switch (nodeTag(plan)) + { + case T_Append: + return ((Append *) plan)->part_prune_info; + case T_MergeAppend: + return ((MergeAppend *) plan)->part_prune_info; + case T_PartitionSelector: + return ((PartitionSelector *) plan)->part_prune_info; + case T_DynamicSeqScan: + return ((DynamicSeqScan *) plan)->part_prune_info; + case T_DynamicIndexScan: + return ((DynamicIndexScan *) plan)->part_prune_info; + case T_DynamicIndexOnlyScan: + return ((DynamicIndexOnlyScan *) plan)->part_prune_info; + case T_DynamicBitmapHeapScan: + return ((DynamicBitmapHeapScan *) plan)->part_prune_info; + case T_DynamicForeignScan: + return ((DynamicForeignScan *) plan)->part_prune_info; + default: + return NULL; + } +} + +static void +check_ppi_fullscan(PartitionPruneInfo *ppi, List *rtable) +{ + ListCell *lc1; + + foreach(lc1, ppi->prune_infos) + { + List *prune_info_list = (List *) lfirst(lc1); + ListCell *lc2; + + foreach(lc2, prune_info_list) + { + PartitionedRelPruneInfo *pinfo = + (PartitionedRelPruneInfo *) lfirst(lc2); + int total = pinfo->nparts; + int present = bms_num_members(pinfo->present_parts); + int threshold = fullscan_threshold; + bool do_reject = false; + + if (total <= 1) + continue; + + if (pinfo->initial_pruning_steps != NIL || + pinfo->exec_pruning_steps != NIL) + continue; + + if (threshold == 0) + do_reject = (present == total); + else + do_reject = (present > threshold); + + if (do_reject) + { + RangeTblEntry *rte = rt_fetch(pinfo->rtindex, rtable); + char *relname = get_rel_name(rte->relid); + char *nspname = get_namespace_name( + get_rel_namespace(rte->relid)); + + reject_fullscan(nspname, relname, present); + } + } + } +} + +/* ---------------------------------------------------------------- + * Check 2: Planner Append/MergeAppend without PartitionPruneInfo + * Covers: no WHERE, WHERE 1=1, WHERE on non-partition-key + * ---------------------------------------------------------------- + */ +static Index +find_partitioned_parent_rti(Bitmapset *apprelids, List *rtable) +{ + int rti = -1; + + while ((rti = bms_next_member(apprelids, rti)) >= 0) + { + RangeTblEntry *rte = rt_fetch(rti, rtable); + + if (rte->rtekind == RTE_RELATION && + rte->inh && + rte->relkind == RELKIND_PARTITIONED_TABLE) + return (Index) rti; + } + return 0; +} + +static int +count_append_subplans(Plan *plan) +{ + if (IsA(plan, Append)) + return list_length(((Append *) plan)->appendplans); + else if (IsA(plan, MergeAppend)) + return list_length(((MergeAppend *) plan)->mergeplans); + return 0; +} + +static void +check_append_no_pruneinfo(Plan *plan, List *rtable) +{ + Bitmapset *apprelids = NULL; + Index parent_rti; + int nsubplans; + int threshold; + + if (IsA(plan, Append)) + apprelids = ((Append *) plan)->apprelids; + else if (IsA(plan, MergeAppend)) + apprelids = ((MergeAppend *) plan)->apprelids; + else + return; + + if (apprelids == NULL) + return; + + parent_rti = find_partitioned_parent_rti(apprelids, rtable); + if (parent_rti == 0) + return; + + nsubplans = count_append_subplans(plan); + threshold = fullscan_threshold; + + if (nsubplans <= 1) + return; + + if (threshold == 0 || nsubplans > threshold) + { + RangeTblEntry *rte = rt_fetch(parent_rti, rtable); + char *relname = get_rel_name(rte->relid); + char *nspname = get_namespace_name( + get_rel_namespace(rte->relid)); + + reject_fullscan(nspname, relname, nsubplans); + } +} + +/* ---------------------------------------------------------------- + * Check 3: ORCA DynamicSeqScan (and other Dynamic nodes) + * ORCA never sets part_prune_info on Dynamic scan nodes. + * We check partOids count vs total partition count. + * Nodes with join_prune_paramids are skipped (JOIN pruning). + * ---------------------------------------------------------------- + */ +static void +check_dynamic_scan_fullscan(Plan *plan, List *rtable) +{ + List *partOids = NIL; + List *join_prune_paramids = NIL; + Index scanrelid = 0; + int nscanned; + int ntotal; + int threshold; + RangeTblEntry *rte; + List *children; + + switch (nodeTag(plan)) + { + case T_DynamicSeqScan: + partOids = ((DynamicSeqScan *) plan)->partOids; + join_prune_paramids = + ((DynamicSeqScan *) plan)->join_prune_paramids; + scanrelid = ((DynamicSeqScan *) plan)->seqscan.scanrelid; + break; + case T_DynamicIndexScan: + partOids = ((DynamicIndexScan *) plan)->partOids; + join_prune_paramids = + ((DynamicIndexScan *) plan)->join_prune_paramids; + scanrelid = ((DynamicIndexScan *) plan)->indexscan.scan.scanrelid; + break; + case T_DynamicIndexOnlyScan: + partOids = ((DynamicIndexOnlyScan *) plan)->partOids; + join_prune_paramids = + ((DynamicIndexOnlyScan *) plan)->join_prune_paramids; + scanrelid = + ((DynamicIndexOnlyScan *) plan)->indexscan.scan.scanrelid; + break; + case T_DynamicBitmapHeapScan: + partOids = ((DynamicBitmapHeapScan *) plan)->partOids; + join_prune_paramids = + ((DynamicBitmapHeapScan *) plan)->join_prune_paramids; + scanrelid = + ((DynamicBitmapHeapScan *) plan)->bitmapheapscan.scan.scanrelid; + break; + case T_DynamicForeignScan: + partOids = ((DynamicForeignScan *) plan)->partOids; + join_prune_paramids = + ((DynamicForeignScan *) plan)->join_prune_paramids; + scanrelid = + ((DynamicForeignScan *) plan)->foreignscan.scan.scanrelid; + break; + default: + return; + } + + /* Skip JOIN dynamic pruning -- runtime selection */ + if (join_prune_paramids != NIL) + return; + + nscanned = list_length(partOids); + if (nscanned <= 1) + return; + + /* Verify this is a partitioned table */ + rte = rt_fetch(scanrelid, rtable); + if (rte->rtekind != RTE_RELATION || + rte->relkind != RELKIND_PARTITIONED_TABLE) + return; + + threshold = fullscan_threshold; + + if (threshold > 0) + { + /* Threshold mode: reject if scanned count > threshold */ + if (nscanned > threshold) + { + char *relname = get_rel_name(rte->relid); + char *nspname = get_namespace_name( + get_rel_namespace(rte->relid)); + + reject_fullscan(nspname, relname, nscanned); + } + return; + } + + /* + * threshold == 0: reject only true full scans (all partitions). + * Compare scanned count against total partition count from catalog. + * Use NoLock since the planner already holds a lock on this rel. + */ + children = find_inheritance_children(rte->relid, NoLock); + ntotal = list_length(children); + list_free(children); + + if (ntotal > 1 && nscanned >= ntotal) + { + char *relname = get_rel_name(rte->relid); + char *nspname = get_namespace_name( + get_rel_namespace(rte->relid)); + + reject_fullscan(nspname, relname, nscanned); + } +} + +/* ---------------------------------------------------------------- + * Plan tree walker + * ---------------------------------------------------------------- + */ +typedef struct rpf_walker_context +{ + plan_tree_base_prefix base; + List *rtable; +} rpf_walker_context; + +static bool +rpf_plan_walker(Node *node, void *context) +{ + rpf_walker_context *ctx = (rpf_walker_context *) context; + + if (node == NULL) + return false; + + if (is_plan_node(node)) + { + Plan *plan = (Plan *) node; + PartitionPruneInfo *ppi = get_part_prune_info(plan); + + if (ppi != NULL) + { + /* Check 1: node has PartitionPruneInfo */ + check_ppi_fullscan(ppi, ctx->rtable); + } + else if (IsA(node, Append) || IsA(node, MergeAppend)) + { + /* Check 2: Planner Append without pruning info */ + check_append_no_pruneinfo(plan, ctx->rtable); + } + else if (IsA(node, DynamicSeqScan) || + IsA(node, DynamicIndexScan) || + IsA(node, DynamicIndexOnlyScan) || + IsA(node, DynamicBitmapHeapScan) || + IsA(node, DynamicForeignScan)) + { + /* Check 3: ORCA Dynamic scan without pruning info */ + check_dynamic_scan_fullscan(plan, ctx->rtable); + } + } + + return plan_tree_walker(node, rpf_plan_walker, context, true); +} + +/* ---------------------------------------------------------------- + * Entry point and planner hook + * ---------------------------------------------------------------- + */ +static void +check_partition_fullscan(PlannedStmt *stmt) +{ + rpf_walker_context ctx; + + if (!reject_fullscan_enabled || !enable_partition_pruning) + return; + + if (stmt->planTree == NULL) + return; + + exec_init_plan_tree_base(&ctx.base, stmt); + ctx.rtable = stmt->rtable; + + rpf_plan_walker((Node *) stmt->planTree, &ctx); +} + +static PlannedStmt * +rpf_planner_hook(Query *parse, + const char *query_string, + int cursorOptions, + ParamListInfo boundParams, + OptimizerOptions *optimizer_options) +{ + PlannedStmt *result; + + if (prev_planner_hook) + result = prev_planner_hook(parse, query_string, cursorOptions, + boundParams, optimizer_options); + else + result = standard_planner(parse, query_string, cursorOptions, + boundParams, optimizer_options); + + if (result != NULL) + check_partition_fullscan(result); + + return result; +} + +void +_PG_init(void) +{ + DefineCustomBoolVariable( + "reject_partition_fullscan", + "Rejects queries that scan all partitions without pruning.", + "When enabled, queries on partitioned tables that cannot " + "prune any partition will be rejected with an error, " + "requiring a WHERE clause on the partition key.", + &reject_fullscan_enabled, + true, + PGC_USERSET, + GUC_EXPLAIN, + NULL, NULL, NULL); + + DefineCustomIntVariable( + "partition_fullscan_threshold", + "Maximum partitions allowed after pruning before rejecting.", + "When reject_partition_fullscan is on, queries are rejected " + "if remaining partitions after pruning exceed this threshold. " + "0 means reject only when no pruning occurs at all.", + &fullscan_threshold, + 0, 0, INT_MAX, + PGC_USERSET, + GUC_EXPLAIN, + NULL, NULL, NULL); + + prev_planner_hook = planner_hook; + planner_hook = rpf_planner_hook; +} + +void +_PG_fini(void) +{ + planner_hook = prev_planner_hook; +} diff --git a/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.control b/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.control new file mode 100644 index 00000000000..9606725636c --- /dev/null +++ b/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.control @@ -0,0 +1,4 @@ +comment = 'reject queries that scan all partitions without pruning' +default_version = '1.0' +module_pathname = '$libdir/reject_partition_fullscan' +relocatable = true diff --git a/gpcontrib/reject_partition_fullscan/sql/partition_fullscan_reject.sql b/gpcontrib/reject_partition_fullscan/sql/partition_fullscan_reject.sql new file mode 100644 index 00000000000..f1c349479eb --- /dev/null +++ b/gpcontrib/reject_partition_fullscan/sql/partition_fullscan_reject.sql @@ -0,0 +1,113 @@ +-- +-- Test reject_partition_fullscan extension +-- +-- Load extension via LOAD (alternative to shared_preload_libraries) +LOAD 'reject_partition_fullscan'; + +-- Create test partitioned table with 3 range partitions +CREATE TABLE pfr_test (id int, dt date, val text) + PARTITION BY RANGE (dt); +CREATE TABLE pfr_test_p1 PARTITION OF pfr_test + FOR VALUES FROM ('2025-01-01') TO ('2025-04-01'); +CREATE TABLE pfr_test_p2 PARTITION OF pfr_test + FOR VALUES FROM ('2025-04-01') TO ('2025-07-01'); +CREATE TABLE pfr_test_p3 PARTITION OF pfr_test + FOR VALUES FROM ('2025-07-01') TO ('2025-10-01'); + +-- Single-partition table for exemption test +CREATE TABLE pfr_single (id int, dt date) + PARTITION BY RANGE (dt); +CREATE TABLE pfr_single_p1 PARTITION OF pfr_single + FOR VALUES FROM ('2025-01-01') TO ('2025-12-31'); + +-- ============================== +-- Test 1: Basic rejection - no WHERE clause +-- ============================== +SET reject_partition_fullscan = on; +SET partition_fullscan_threshold = 0; + +SELECT * FROM pfr_test; +SELECT count(*) FROM pfr_test; + +-- ============================== +-- Test 2: Pruning passes - WHERE on partition key +-- ============================== +SELECT * FROM pfr_test WHERE dt = '2025-02-01'; +SELECT * FROM pfr_test + WHERE dt >= '2025-01-01' AND dt < '2025-04-01'; + +-- ============================== +-- Test 3: WHERE not on partition key - should reject +-- ============================== +SELECT * FROM pfr_test WHERE val = 'x'; +SELECT * FROM pfr_test WHERE id = 1; + +-- ============================== +-- Test 4: WHERE 1=1 - should reject (constant folded to NIL) +-- ============================== +SELECT * FROM pfr_test WHERE 1 = 1; +SELECT * FROM pfr_test WHERE true; + +-- ============================== +-- Test 5: GUC off - allow full scan +-- ============================== +SET reject_partition_fullscan = off; +SELECT * FROM pfr_test; +SET reject_partition_fullscan = on; + +-- ============================== +-- Test 6: enable_partition_pruning=off exemption +-- ============================== +SET enable_partition_pruning = off; +SELECT * FROM pfr_test; +SET enable_partition_pruning = on; + +-- ============================== +-- Test 7: Single-partition table exemption +-- ============================== +SELECT * FROM pfr_single; + +-- ============================== +-- Test 8: Threshold mode +-- ============================== +SET partition_fullscan_threshold = 2; + +-- Pruned to 2 partitions, within threshold, should pass +SELECT * FROM pfr_test + WHERE dt >= '2025-01-01' AND dt < '2025-07-01'; + +-- All 3 partitions exceed threshold of 2, should reject +SELECT * FROM pfr_test; + +SET partition_fullscan_threshold = 0; + +-- ============================== +-- Test 9: Prepared statement with parameter (exemption) +-- ============================== +PREPARE pfr_q AS SELECT * FROM pfr_test WHERE dt = $1; +EXECUTE pfr_q('2025-02-01'); +DEALLOCATE pfr_q; + +-- ============================== +-- Test 10: UPDATE/DELETE without WHERE - should reject +-- ============================== +UPDATE pfr_test SET val = 'y'; +DELETE FROM pfr_test; + +-- UPDATE/DELETE with partition key - should pass +UPDATE pfr_test SET val = 'y' WHERE dt = '2025-02-01'; +DELETE FROM pfr_test WHERE dt = '2025-02-01'; + +-- ============================== +-- Test 11: Subquery containing partitioned table +-- ============================== +SELECT * FROM (SELECT * FROM pfr_test) sub; + +-- ============================== +-- Cleanup +-- ============================== +DROP TABLE pfr_test; +DROP TABLE pfr_single; +RESET reject_partition_fullscan; +RESET partition_fullscan_threshold; +RESET enable_partition_pruning; From 3d20b0e86b3fb0c9bfba17ee1792717d79b96a10 Mon Sep 17 00:00:00 2001 From: liu qiren <13559537330@163.com> Date: Sat, 2 May 2026 15:07:00 +0800 Subject: [PATCH 2/4] Fix: only build reject_partition_fullscan with debug extensions Per reviewer feedback, restrict the extension to build only when enable_debug_extensions=yes, rather than in all builds. --- gpcontrib/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gpcontrib/Makefile b/gpcontrib/Makefile index cce33927ad9..16ccc9f5ccd 100644 --- a/gpcontrib/Makefile +++ b/gpcontrib/Makefile @@ -31,8 +31,7 @@ else gp_legacy_string_agg \ gp_exttable_fdw \ gp_toolkit \ - pg_hint_plan \ - reject_partition_fullscan + pg_hint_plan endif ifeq "$(with_diskquota)" "yes" From f632544b60971b5745c14ba8b7d3302ed947c735 Mon Sep 17 00:00:00 2001 From: liu qiren <13559537330@163.com> Date: Mon, 4 May 2026 15:06:49 +0800 Subject: [PATCH 3/4] Doc: add standard ASF license headers to all new files Add the Apache Software Foundation license header to all newly created files per project contribution requirements. --- gpcontrib/reject_partition_fullscan/Makefile | 17 ++++++++++++++ .../reject_partition_fullscan--1.0.sql | 22 +++++++++++++++++++ .../reject_partition_fullscan.c | 22 +++++++++++++++++-- .../reject_partition_fullscan.control | 17 ++++++++++++++ .../sql/partition_fullscan_reject.sql | 19 ++++++++++++++++ 5 files changed, 95 insertions(+), 2 deletions(-) diff --git a/gpcontrib/reject_partition_fullscan/Makefile b/gpcontrib/reject_partition_fullscan/Makefile index 3e762c4c1f5..65e08e4b186 100644 --- a/gpcontrib/reject_partition_fullscan/Makefile +++ b/gpcontrib/reject_partition_fullscan/Makefile @@ -1,3 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + MODULE_big = reject_partition_fullscan OBJS = reject_partition_fullscan.o diff --git a/gpcontrib/reject_partition_fullscan/reject_partition_fullscan--1.0.sql b/gpcontrib/reject_partition_fullscan/reject_partition_fullscan--1.0.sql index 91ce4676f67..c8093e8d18c 100644 --- a/gpcontrib/reject_partition_fullscan/reject_partition_fullscan--1.0.sql +++ b/gpcontrib/reject_partition_fullscan/reject_partition_fullscan--1.0.sql @@ -1,3 +1,25 @@ +/*------------------------------------------------------------------------- + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + *------------------------------------------------------------------------- + */ + /* gpcontrib/reject_partition_fullscan/reject_partition_fullscan--1.0.sql */ -- complain if script is sourced in psql, rather than via CREATE EXTENSION diff --git a/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.c b/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.c index 332a300ef1a..0670c8f6232 100644 --- a/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.c +++ b/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.c @@ -1,6 +1,24 @@ /*------------------------------------------------------------------------- + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. * * reject_partition_fullscan.c + * * Extension to reject queries that scan all partitions of a * partitioned table without effective partition pruning. * @@ -32,8 +50,8 @@ * reject_partition_fullscan (bool, default true) * partition_fullscan_threshold (int, default 0) * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. + * IDENTIFICATION + * gpcontrib/reject_partition_fullscan/reject_partition_fullscan.c * *------------------------------------------------------------------------- */ diff --git a/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.control b/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.control index 9606725636c..7d925b76b84 100644 --- a/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.control +++ b/gpcontrib/reject_partition_fullscan/reject_partition_fullscan.control @@ -1,3 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + comment = 'reject queries that scan all partitions without pruning' default_version = '1.0' module_pathname = '$libdir/reject_partition_fullscan' diff --git a/gpcontrib/reject_partition_fullscan/sql/partition_fullscan_reject.sql b/gpcontrib/reject_partition_fullscan/sql/partition_fullscan_reject.sql index f1c349479eb..537d70f7501 100644 --- a/gpcontrib/reject_partition_fullscan/sql/partition_fullscan_reject.sql +++ b/gpcontrib/reject_partition_fullscan/sql/partition_fullscan_reject.sql @@ -1,3 +1,22 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + -- -- Test reject_partition_fullscan extension -- From cc58e54973cade7bdc970d5f173da62d2f4820c7 Mon Sep 17 00:00:00 2001 From: liu qiren <13559537330@163.com> Date: Mon, 4 May 2026 15:11:13 +0800 Subject: [PATCH 4/4] Fix: add RAT exclusions for extension Makefile and control file Add reject_partition_fullscan's Makefile and .control file to the Apache RAT exclusion list in pom.xml, consistent with how other gpcontrib extensions handle these file types. --- pom.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 633b07afe8e..52e3bda9473 100644 --- a/pom.xml +++ b/pom.xml @@ -1276,7 +1276,10 @@ code or new licensing patterns. gpcontrib/gp_stats_collector/.clang-format gpcontrib/gp_stats_collector/Makefile - contrib/pax_storage/src/test/**