diff --git a/src/gausskernel/optimizer/plan/createplan.cpp b/src/gausskernel/optimizer/plan/createplan.cpp index a55d76cd5..9ada54aa5 100755 --- a/src/gausskernel/optimizer/plan/createplan.cpp +++ b/src/gausskernel/optimizer/plan/createplan.cpp @@ -222,6 +222,8 @@ static bool relIsDeltaNode(PlannerInfo* root, RelOptInfo* relOptInfo); static void ModifyWorktableWtParam(Node* planNode, int oldWtParam, int newWtParam); +static bool ScanQualsViolateNotNullConstr(PlannerInfo* root, RelOptInfo* rel, Path* best_path); + #define SATISFY_INFORMATIONAL_CONSTRAINT(joinPlan, joinType) \ (u_sess->attr.attr_sql.enable_constraint_optimization && true == innerPlan((joinPlan))->hasUniqueResults && \ JOIN_SEMI != (joinType) && JOIN_ANTI != (joinType)) @@ -752,11 +754,49 @@ static Plan* create_scan_plan(PlannerInfo* root, Path* best_path) */ if (root->hasPseudoConstantQuals) { plan = create_gating_plan(root, plan, scan_clauses); + } else if (ScanQualsViolateNotNullConstr(root, rel, best_path)) { + /* + * If there is IS NULL qual on a known NOT-NULL attribute, insert a Result node to prevent needless execution. + */ + plan = (Plan*)make_result(root, plan->targetlist, (Node*)list_make1(makeBoolConst(false, false)), plan); } return plan; } +static bool IsScanPath(NodeTag type) +{ + return ( + type == T_CStoreScan || type == T_CStoreIndexScan || type == T_CStoreIndexHeapScan || type == T_SeqScan || + type == T_DfsScan || type == T_IndexScan || type == T_IndexOnlyScan || type == T_BitmapHeapScan + ); +} + +static bool ScanQualsViolateNotNullConstr(PlannerInfo* root, RelOptInfo* rel, Path* best_path) +{ + /* For now, we only support table scan optimization */ + if (rel->rtekind != RTE_RELATION || !IsScanPath(best_path->pathtype)) { + return false; + } + + List* scan_clauses = rel->baserestrictinfo; + ListCell* lc = NULL; + foreach (lc, scan_clauses) { + Node* clause = (Node*)lfirst(lc); + if (IsA(clause, RestrictInfo) && IsA(((RestrictInfo*)clause)->clause, NullTest)) { + NullTest* expr = (NullTest*)((RestrictInfo*)clause)->clause; + /* For attribute with NOT-NULL constraint, IS-NULL expression can be short-circuited */ + if (expr->nulltesttype != IS_NULL || !IsA(expr->arg, Var)) { + continue; + } + if (check_var_nonnullable(root->parse, (Node*)expr->arg)) { + return true; + } + } + } + return false; +} + /* * Build a target list (ie, a list of TargetEntry) for a relation. */ diff --git a/src/test/regress/expected/null_test_opt.out b/src/test/regress/expected/null_test_opt.out new file mode 100755 index 000000000..7ffa097f8 --- /dev/null +++ b/src/test/regress/expected/null_test_opt.out @@ -0,0 +1,100 @@ +create schema null_test_opt_nsp; +set search_path = null_test_opt_nsp; +create table t(a int, b int not null); +insert into t values(1, 1); +explain (costs off) select * from t where b is null order by 1, 2; + QUERY PLAN +----------------------------------- + Sort + Sort Key: a, b + -> Result + One-Time Filter: false + -> Seq Scan on t + Filter: (b IS NULL) +(6 rows) + +select * from t where b is null order by 1, 2; + a | b +---+--- +(0 rows) + +explain (costs off) select * from t where b is not null order by 1, 2; + QUERY PLAN +--------------------- + Sort + Sort Key: a, b + -> Seq Scan on t +(3 rows) + +select * from t where b is not null order by 1, 2; + a | b +---+--- + 1 | 1 +(1 row) + +explain (costs off) select * from t where b is null or b is not null order by 1, 2; + QUERY PLAN +--------------------- + Sort + Sort Key: a, b + -> Seq Scan on t +(3 rows) + +select * from t where b is null or b is not null order by 1, 2; + a | b +---+--- + 1 | 1 +(1 row) + +explain (costs off) select * from t where b is null or a = 1 order by 1, 2; + QUERY PLAN +------------------------------------------ + Sort + Sort Key: a, b + -> Seq Scan on t + Filter: ((b IS NULL) OR (a = 1)) +(4 rows) + +select * from t where b is null or a = 1 order by 1, 2; + a | b +---+--- + 1 | 1 +(1 row) + +explain (costs off) select * from t where b is null and a = 1 order by 1, 2; + QUERY PLAN +------------------------------------------------- + Sort + Sort Key: b + -> Result + One-Time Filter: false + -> Seq Scan on t + Filter: ((b IS NULL) AND (a = 1)) +(6 rows) + +select * from t where b is null and a = 1 order by 1, 2; + a | b +---+--- +(0 rows) + +explain (costs off) select * from t tt1 join t tt2 on tt1.a = tt2.b where tt1.a is null; + QUERY PLAN +----------------------------------------- + Hash Join + Hash Cond: (tt1.a = tt2.b) + -> Seq Scan on t tt1 + Filter: (a IS NULL) + -> Hash + -> Result + One-Time Filter: false + -> Seq Scan on t tt2 + Filter: (b IS NULL) +(9 rows) + +select * from t tt1 join t tt2 on tt1.a = tt2.b where tt1.a is null; + a | b | a | b +---+---+---+--- +(0 rows) + +drop schema null_test_opt_nsp cascade; +NOTICE: drop cascades to table t diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index f96fce2de..0a5e57e52 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -25,8 +25,8 @@ test: pldeveloper_gs_source test: index_advisor test: pl_debugger_server pl_debugger_client test: update_for_wait_s1 update_for_wait_s2 -test: plan_hint plan_hint_set plan_hint_no_expand plan_hint_iud -test: large_sequence int16 gs_dump_sequence +test: plan_hint plan_hint_set plan_hint_no_expand plan_hint_iud null_test_opt +test: large_sequence int16 forall_save_exceptions gs_dump_sequence test: gs_dump_tableof test: analyze_commands #test: single_node_job diff --git a/src/test/regress/sql/null_test_opt.sql b/src/test/regress/sql/null_test_opt.sql new file mode 100644 index 000000000..2c242cc23 --- /dev/null +++ b/src/test/regress/sql/null_test_opt.sql @@ -0,0 +1,33 @@ +create schema null_test_opt_nsp; + +set search_path = null_test_opt_nsp; + +create table t(a int, b int not null); + +insert into t values(1, 1); + +explain (costs off) select * from t where b is null order by 1, 2; + +select * from t where b is null order by 1, 2; + +explain (costs off) select * from t where b is not null order by 1, 2; + +select * from t where b is not null order by 1, 2; + +explain (costs off) select * from t where b is null or b is not null order by 1, 2; + +select * from t where b is null or b is not null order by 1, 2; + +explain (costs off) select * from t where b is null or a = 1 order by 1, 2; + +select * from t where b is null or a = 1 order by 1, 2; + +explain (costs off) select * from t where b is null and a = 1 order by 1, 2; + +select * from t where b is null and a = 1 order by 1, 2; + +explain (costs off) select * from t tt1 join t tt2 on tt1.a = tt2.b where tt1.a is null; + +select * from t tt1 join t tt2 on tt1.a = tt2.b where tt1.a is null; + +drop schema null_test_opt_nsp cascade; \ No newline at end of file