From 8faccc420c9d39901a69b743ba979b68381aac6c Mon Sep 17 00:00:00 2001 From: bovlb <31326650+bovlb@users.noreply.github.com> Date: Sun, 3 Aug 2025 21:10:03 +0000 Subject: [PATCH 01/10] Add extractClauseFromBooleanTest --- src/query.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/query.c b/src/query.c index d2dd779..dfd60b5 100644 --- a/src/query.c +++ b/src/query.c @@ -34,6 +34,8 @@ void extractClauseFromScalarArrayOpExpr( ScalarArrayOpExpr *node, List **quals); +void extractClauseFromBooleanTest(Relids base_relids, BooleanTest *node, List **quals); + char *getOperatorString(Oid opoid); MulticornBaseQual *makeQual(AttrNumber varattno, char *opname, Expr *value, @@ -319,6 +321,11 @@ extractRestrictions( #endif base_relids, (ScalarArrayOpExpr *) node, quals); break; + + case T_BooleanTest: + extractClauseFromBooleanTest(base_relids, (BooleanTest *) node, quals); + break; + default: { ereport(WARNING, @@ -446,6 +453,50 @@ extractClauseFromNullTest(Relids base_relids, } } +/* + * Convert a "BoolExpr" (IS TRUE, IS FALSE, IS NOT TRUE, IS NOT FALSE) + * to the corresponding qualifier. + */ +void extractClauseFromBooleanTest(Relids base_relids, BooleanTest *node, List **quals) { + elog(DEBUG3, "entering extractClauseFromBooleanTest()"); + if (IsA(node->arg, Var)) + { + Var *var = (Var *) node->arg; + MulticornBaseQual *result; + char *opname = NULL; + Expr *val; + + if (var->varattno < 1) + { + return; + } + switch (node->booltesttype) + { + case IS_TRUE: + opname = "IS"; + val = (Expr *) makeConst(BOOLOID, -1, InvalidOid, sizeof(bool), BoolGetDatum(true), false, true); + break; + case IS_FALSE: + opname = "IS"; + val = (Expr *) makeConst(BOOLOID, -1, InvalidOid, sizeof(bool), BoolGetDatum(false), false, true); + break; + case IS_NOT_TRUE: + opname = "IS NOT"; + val = (Expr *) makeConst(BOOLOID, -1, InvalidOid, sizeof(bool), BoolGetDatum(true), false, true); + break; + case IS_NOT_FALSE: + opname = "IS NOT"; + val = (Expr *) makeConst(BOOLOID, -1, InvalidOid, sizeof(bool), BoolGetDatum(true), false, true); + break; + default: + /* IS UNKNOWN, IS NOT UNKNOWN */ + elog(ERROR, "unsupported boolean test type %d", node->booltesttype); + } + result = makeQual(var->varattno, opname, + (Expr *) val, false, false); + *quals = lappend(*quals, result); + } +} /* From 024894d86df66d2a496ff06de27b3e3a9c32dab2 Mon Sep 17 00:00:00 2001 From: bovlb <31326650+bovlb@users.noreply.github.com> Date: Sun, 3 Aug 2025 16:08:32 -0700 Subject: [PATCH 02/10] Add support for Var nodes in query extraction --- src/query.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/query.c b/src/query.c index dfd60b5..d89d417 100644 --- a/src/query.c +++ b/src/query.c @@ -36,7 +36,9 @@ void extractClauseFromScalarArrayOpExpr( void extractClauseFromBooleanTest(Relids base_relids, BooleanTest *node, List **quals); -char *getOperatorString(Oid opoid); +void extractClauseFromVar(Relids base_relids, Var *node, List **quals); + +char *getOperatorString(Oid opoid); MulticornBaseQual *makeQual(AttrNumber varattno, char *opname, Expr *value, bool isarray, @@ -322,10 +324,14 @@ extractRestrictions( base_relids, (ScalarArrayOpExpr *) node, quals); break; - case T_BooleanTest: - extractClauseFromBooleanTest(base_relids, (BooleanTest *) node, quals); + case T_BooleanTest: + extractClauseFromBooleanTest(base_relids, (BooleanTest *) node, quals); break; + case T_Var: + extractClauseFromVar(base_relids, (Var *) node, quals); + break; + default: { ereport(WARNING, @@ -498,6 +504,22 @@ void extractClauseFromBooleanTest(Relids base_relids, BooleanTest *node, List ** } } +void extractClauseFromVar(Relids base_relids, Var *node, List **quals) +{ + if (!bms_is_subset(pull_varnos((Node *) node), base_relids)) + return; + + RestrictInfo *info = makeSimpleRestrictInfo( + (Expr *) node, + true, + false, + false, + NULL, + base_relids, + NULL); + + *quals = lappend(*quals, info); +} /* * Returns a "Value" node containing the string name of the column from a var. From 412b2813c748c0bb822e67362470cd4fd0c57f05 Mon Sep 17 00:00:00 2001 From: bovlb <31326650+bovlb@users.noreply.github.com> Date: Sun, 3 Aug 2025 16:15:08 -0700 Subject: [PATCH 03/10] add root --- src/query.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/query.c b/src/query.c index d89d417..e95b5c2 100644 --- a/src/query.c +++ b/src/query.c @@ -329,7 +329,11 @@ extractRestrictions( break; case T_Var: - extractClauseFromVar(base_relids, (Var *) node, quals); + extractClauseFromVar( +#if PG_VERSION_NUM >= 140000 + (PlannerInfo *) root, +#endif + base_relids, (Var *) node, quals); break; default: @@ -463,7 +467,8 @@ extractClauseFromNullTest(Relids base_relids, * Convert a "BoolExpr" (IS TRUE, IS FALSE, IS NOT TRUE, IS NOT FALSE) * to the corresponding qualifier. */ -void extractClauseFromBooleanTest(Relids base_relids, BooleanTest *node, List **quals) { +void extractClauseFromBooleanTest(Relids base_relids, BooleanTest *node, List **quals) +{ elog(DEBUG3, "entering extractClauseFromBooleanTest()"); if (IsA(node->arg, Var)) { @@ -504,9 +509,17 @@ void extractClauseFromBooleanTest(Relids base_relids, BooleanTest *node, List ** } } -void extractClauseFromVar(Relids base_relids, Var *node, List **quals) +void extractClauseFromVar( +#if PG_VERSION_NUM >= 140000 + PlannerInfo *root, +#endif + Relids base_relids, Var *node, List **quals) { - if (!bms_is_subset(pull_varnos((Node *) node), base_relids)) + if (!bms_is_subset(pull_varnos( +#if PG_VERSION_NUM >= 140000 + root, +#endif + (Node *) node), base_relids)) return; RestrictInfo *info = makeSimpleRestrictInfo( From 0ce5ce32a3e1e60143f54eaf4bb211e1e192afc3 Mon Sep 17 00:00:00 2001 From: bovlb <31326650+bovlb@users.noreply.github.com> Date: Sun, 3 Aug 2025 16:17:01 -0700 Subject: [PATCH 04/10] one more place --- src/query.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/query.c b/src/query.c index e95b5c2..d392d58 100644 --- a/src/query.c +++ b/src/query.c @@ -36,7 +36,11 @@ void extractClauseFromScalarArrayOpExpr( void extractClauseFromBooleanTest(Relids base_relids, BooleanTest *node, List **quals); -void extractClauseFromVar(Relids base_relids, Var *node, List **quals); +void extractClauseFromVar( +#if PG_VERSION_NUM >= 140000 + PlannerInfo *root, +#endif + Relids base_relids, Var *node, List **quals); char *getOperatorString(Oid opoid); From b8c60ca546c3bd5aa5a576b4430b459101feb31b Mon Sep 17 00:00:00 2001 From: bovlb <31326650+bovlb@users.noreply.github.com> Date: Sun, 3 Aug 2025 16:21:01 -0700 Subject: [PATCH 05/10] try this --- src/query.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/query.c b/src/query.c index d392d58..1ebf7f1 100644 --- a/src/query.c +++ b/src/query.c @@ -526,16 +526,16 @@ void extractClauseFromVar( (Node *) node), base_relids)) return; - RestrictInfo *info = makeSimpleRestrictInfo( - (Expr *) node, - true, - false, - false, - NULL, - base_relids, - NULL); - - *quals = lappend(*quals, info); + Expr *true_expr = (Expr *) makeConst(BOOLOID, // Type OID for boolean + -1, // typmod + InvalidOid, // collation + sizeof(bool), // constlen + BoolGetDatum(true), // the actual value + false, // isnull + true); // constbyval + + result = makeQual(var->varattno, "=", true_expr, false, false); + *quals = lappend(*quals, result); } /* From c46de571455d65a4a761b7e125c2a8d2e6f3b9e5 Mon Sep 17 00:00:00 2001 From: bovlb <31326650+bovlb@users.noreply.github.com> Date: Sun, 3 Aug 2025 16:23:26 -0700 Subject: [PATCH 06/10] this --- src/query.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/query.c b/src/query.c index 1ebf7f1..f8c728a 100644 --- a/src/query.c +++ b/src/query.c @@ -519,21 +519,24 @@ void extractClauseFromVar( #endif Relids base_relids, Var *node, List **quals) { + MulticornBaseQual *result; + Expr *true_expr; if (!bms_is_subset(pull_varnos( #if PG_VERSION_NUM >= 140000 root, #endif - (Node *) node), base_relids)) + (Node *) node), base_relids)) { return; + } + + true_expr = (Expr *) makeConst(BOOLOID, // Type OID for boolean + -1, // typmod + InvalidOid, // collation + sizeof(bool), // constlen + BoolGetDatum(true), // the actual value + false, // isnull + true); // constbyval - Expr *true_expr = (Expr *) makeConst(BOOLOID, // Type OID for boolean - -1, // typmod - InvalidOid, // collation - sizeof(bool), // constlen - BoolGetDatum(true), // the actual value - false, // isnull - true); // constbyval - result = makeQual(var->varattno, "=", true_expr, false, false); *quals = lappend(*quals, result); } From bcb1e55f550d54de69b359e45756afb422efbf01 Mon Sep 17 00:00:00 2001 From: bovlb <31326650+bovlb@users.noreply.github.com> Date: Sun, 3 Aug 2025 16:25:50 -0700 Subject: [PATCH 07/10] . --- src/query.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/query.c b/src/query.c index f8c728a..1b9d3d0 100644 --- a/src/query.c +++ b/src/query.c @@ -517,7 +517,7 @@ void extractClauseFromVar( #if PG_VERSION_NUM >= 140000 PlannerInfo *root, #endif - Relids base_relids, Var *node, List **quals) + Relids base_relids, Var *var, List **quals) { MulticornBaseQual *result; Expr *true_expr; @@ -525,7 +525,7 @@ void extractClauseFromVar( #if PG_VERSION_NUM >= 140000 root, #endif - (Node *) node), base_relids)) { + (Node *) var), base_relids)) { return; } From bf8f92bd2417a4cbb4df3f6e0fe2500b97fddda5 Mon Sep 17 00:00:00 2001 From: bovlb <31326650+bovlb@users.noreply.github.com> Date: Mon, 4 Aug 2025 02:54:23 +0000 Subject: [PATCH 08/10] Add support for IS_UNKNOWN and IS_NOT_UNKNOWN; fix whitespace --- src/query.c | 63 +++++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/src/query.c b/src/query.c index 1b9d3d0..54d899b 100644 --- a/src/query.c +++ b/src/query.c @@ -16,11 +16,11 @@ void extractClauseFromOpExpr( #if PG_VERSION_NUM >= 140000 - PlannerInfo *root, + PlannerInfo *root, #endif - Relids base_relids, - OpExpr *node, - List **quals); + Relids base_relids, + OpExpr *node, + List **quals); void extractClauseFromNullTest(Relids base_relids, NullTest *node, @@ -28,17 +28,17 @@ void extractClauseFromNullTest(Relids base_relids, void extractClauseFromScalarArrayOpExpr( #if PG_VERSION_NUM >= 140000 - PlannerInfo *root, + PlannerInfo *root, #endif - Relids base_relids, - ScalarArrayOpExpr *node, - List **quals); + Relids base_relids, + ScalarArrayOpExpr *node, + List **quals); void extractClauseFromBooleanTest(Relids base_relids, BooleanTest *node, List **quals); void extractClauseFromVar( #if PG_VERSION_NUM >= 140000 - PlannerInfo *root, + PlannerInfo *root, #endif Relids base_relids, Var *node, List **quals); @@ -333,12 +333,12 @@ extractRestrictions( break; case T_Var: - extractClauseFromVar( + extractClauseFromVar( #if PG_VERSION_NUM >= 140000 (PlannerInfo *) root, #endif base_relids, (Var *) node, quals); - break; + break; default: { @@ -364,9 +364,9 @@ extractRestrictions( void extractClauseFromOpExpr( #if PG_VERSION_NUM >= 140000 - PlannerInfo *root, + PlannerInfo *root, #endif - Relids base_relids, OpExpr *op, List **quals) + Relids base_relids, OpExpr *op, List **quals) { Var *left; Expr *right; @@ -399,11 +399,11 @@ extractClauseFromOpExpr( void extractClauseFromScalarArrayOpExpr( #if PG_VERSION_NUM >= 140000 - PlannerInfo *root, + PlannerInfo *root, #endif - Relids base_relids, - ScalarArrayOpExpr *op, - List **quals) + Relids base_relids, + ScalarArrayOpExpr *op, + List **quals) { Var *left; Expr *right; @@ -417,9 +417,9 @@ extractClauseFromScalarArrayOpExpr( if (!(contain_volatile_functions((Node *) right) || bms_is_subset(base_relids, pull_varnos( #if PG_VERSION_NUM >= 140000 - root, + root, #endif - (Node *) right)))) + (Node *) right)))) { *quals = lappend(*quals, makeQual(left->varattno, @@ -468,7 +468,7 @@ extractClauseFromNullTest(Relids base_relids, } /* - * Convert a "BoolExpr" (IS TRUE, IS FALSE, IS NOT TRUE, IS NOT FALSE) + * Convert a "BooleanTest" (IS TRUE, IS FALSE, IS NOT TRUE, IS NOT FALSE) * to the corresponding qualifier. */ void extractClauseFromBooleanTest(Relids base_relids, BooleanTest *node, List **quals) @@ -503,8 +503,15 @@ void extractClauseFromBooleanTest(Relids base_relids, BooleanTest *node, List ** opname = "IS NOT"; val = (Expr *) makeConst(BOOLOID, -1, InvalidOid, sizeof(bool), BoolGetDatum(true), false, true); break; + case IS_UNKNOWN: + opname = "IS"; + val = (Expr *) makeConst(BOOLOID, -1, InvalidOid, sizeof(bool), (Datum)0, true, true); + break; + case IS_NOT_UNKNOWN: + opname = "IS NOT"; + val = (Expr *) makeConst(BOOLOID, -1, InvalidOid, sizeof(bool), (Datum)0, true, true); + break; default: - /* IS UNKNOWN, IS NOT UNKNOWN */ elog(ERROR, "unsupported boolean test type %d", node->booltesttype); } result = makeQual(var->varattno, opname, @@ -515,18 +522,18 @@ void extractClauseFromBooleanTest(Relids base_relids, BooleanTest *node, List ** void extractClauseFromVar( #if PG_VERSION_NUM >= 140000 - PlannerInfo *root, + PlannerInfo *root, #endif Relids base_relids, Var *var, List **quals) { MulticornBaseQual *result; Expr *true_expr; - if (!bms_is_subset(pull_varnos( + if (!bms_is_subset(pull_varnos( #if PG_VERSION_NUM >= 140000 root, #endif (Node *) var), base_relids)) { - return; + return; } true_expr = (Expr *) makeConst(BOOLOID, // Type OID for boolean @@ -538,7 +545,7 @@ void extractClauseFromVar( true); // constbyval result = makeQual(var->varattno, "=", true_expr, false, false); - *quals = lappend(*quals, result); + *quals = lappend(*quals, result); } /* @@ -577,7 +584,7 @@ makeQual(AttrNumber varattno, char *opname, Expr *value, bool isarray, switch (value->type) { case T_Const: - elog(DEBUG3, "T_Const"); + elog(DEBUG3, "T_Const"); qual = palloc0(sizeof(MulticornConstQual)); qual->right_type = T_Const; qual->typeoid = ((Const *) value)->consttype; @@ -585,13 +592,13 @@ makeQual(AttrNumber varattno, char *opname, Expr *value, bool isarray, ((MulticornConstQual *) qual)->isnull = ((Const *) value)->constisnull; break; case T_Var: - elog(DEBUG3, "T_Var"); + elog(DEBUG3, "T_Var"); qual = palloc0(sizeof(MulticornVarQual)); qual->right_type = T_Var; ((MulticornVarQual *) qual)->rightvarattno = ((Var *) value)->varattno; break; default: - elog(DEBUG3, "default"); + elog(DEBUG3, "default"); qual = palloc0(sizeof(MulticornParamQual)); qual->right_type = T_Param; ((MulticornParamQual *) qual)->expr = value; From 5ec517b7cbd368d5f9f57e67b2644a174cf4e2b5 Mon Sep 17 00:00:00 2001 From: bovlb <31326650+bovlb@users.noreply.github.com> Date: Mon, 4 Aug 2025 03:42:16 +0000 Subject: [PATCH 09/10] Add support for NOT Var --- src/query.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/query.c b/src/query.c index 54d899b..d5bd922 100644 --- a/src/query.c +++ b/src/query.c @@ -36,6 +36,12 @@ void extractClauseFromScalarArrayOpExpr( void extractClauseFromBooleanTest(Relids base_relids, BooleanTest *node, List **quals); +void extractClauseFromBoolExpr( +#if PG_VERSION_NUM >= 140000 + PlannerInfo *root, +#endif + Relids base_relids, BoolExpr *node, List **quals); + void extractClauseFromVar( #if PG_VERSION_NUM >= 140000 PlannerInfo *root, @@ -340,6 +346,13 @@ extractRestrictions( base_relids, (Var *) node, quals); break; + case T_BoolExpr: + extractClauseFromBoolExpr( +#if PG_VERSION_NUM >= 140000 + root, +#endif + base_relids, (BoolExpr *) node, quals); + break; default: { ereport(WARNING, @@ -548,6 +561,35 @@ void extractClauseFromVar( *quals = lappend(*quals, result); } +void extractClauseFromBoolExpr( +#if PG_VERSION_NUM >= 140000 + PlannerInfo *root, +#endif + Relids base_relids, BoolExpr *bexpr, List **quals) +{ + if (!bms_is_subset(pull_varnos( +#if PG_VERSION_NUM >= 140000 + root, +#endif + (Node *) bexpr), base_relids)) { + return; + } + + if (bexpr->boolop == NOT_EXPR && + list_length(bexpr->args) == 1 && + IsA(linitial(bexpr->args), Var)) { + + Var *var = (Var *) linitial(bexpr->args); + Expr *true_expr = (Expr *) makeConst( + BOOLOID, -1, InvalidOid, sizeof(bool), + BoolGetDatum(true), false, true); + + MulticornBaseQual *result = makeQual( + var->varattno, "<>", true_expr, false, false); + + *quals = lappend(*quals, result); + }} + /* * Returns a "Value" node containing the string name of the column from a var. */ From 45050952b51c94c409349909f45611a04959b2d4 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Mon, 4 Aug 2025 08:11:30 -0600 Subject: [PATCH 10/10] add bool operator pushdown test cases --- .../expected/multicorn_regression_test.out | 60 +++++++++++++++++++ test-3.9/sql/multicorn_regression_test.sql | 11 ++++ 2 files changed, 71 insertions(+) diff --git a/test-3.9/expected/multicorn_regression_test.out b/test-3.9/expected/multicorn_regression_test.out index 54f3b24..f60a99a 100644 --- a/test-3.9/expected/multicorn_regression_test.out +++ b/test-3.9/expected/multicorn_regression_test.out @@ -473,6 +473,66 @@ NOTICE: ['test1', 'test2', 'test3'] -------+-------+------- (0 rows) +-- Test boolean operation pushdown +ALTER FOREIGN TABLE testmulticorn alter test1 type bool; +select * from testmulticorn where test1; +NOTICE: [('option1', 'option1'), ('test_type', 'iter_none'), ('usermapping', 'test')] +NOTICE: [('test1', 'boolean'), ('test2', 'bytea'), ('test3', 'money')] +NOTICE: [test1 = t] +NOTICE: ['test1', 'test2', 'test3'] + test1 | test2 | test3 +-------+-------+------- +(0 rows) + +select * from testmulticorn where NOT test1; +NOTICE: [test1 <> t] +NOTICE: ['test1', 'test2', 'test3'] + test1 | test2 | test3 +-------+-------+------- +(0 rows) + +select * from testmulticorn where test1 = TRUE; +NOTICE: [test1 = t] +NOTICE: ['test1', 'test2', 'test3'] + test1 | test2 | test3 +-------+-------+------- +(0 rows) + +select * from testmulticorn where test1 = FALSE; +NOTICE: [test1 <> t] +NOTICE: ['test1', 'test2', 'test3'] + test1 | test2 | test3 +-------+-------+------- +(0 rows) + +select * from testmulticorn where test1 IS TRUE; +NOTICE: [test1 IS t] +NOTICE: ['test1', 'test2', 'test3'] + test1 | test2 | test3 +-------+-------+------- +(0 rows) + +select * from testmulticorn where test1 IS FALSE; +NOTICE: [test1 IS f] +NOTICE: ['test1', 'test2', 'test3'] + test1 | test2 | test3 +-------+-------+------- +(0 rows) + +select * from testmulticorn where test1 IS UNKNOWN; +NOTICE: [test1 IS None] +NOTICE: ['test1', 'test2', 'test3'] + test1 | test2 | test3 +-------+-------+------- +(0 rows) + +select * from testmulticorn where test1 IS NOT UNKNOWN; +NOTICE: [test1 IS NOT None] +NOTICE: ['test1', 'test2', 'test3'] + test1 | test2 | test3 +-------+-------+------- +(0 rows) + DROP USER MAPPING FOR current_user SERVER multicorn_srv; DROP EXTENSION multicorn cascade; NOTICE: drop cascades to 3 other objects diff --git a/test-3.9/sql/multicorn_regression_test.sql b/test-3.9/sql/multicorn_regression_test.sql index 602c01b..18d3f5b 100644 --- a/test-3.9/sql/multicorn_regression_test.sql +++ b/test-3.9/sql/multicorn_regression_test.sql @@ -107,5 +107,16 @@ ALTER FOREIGN TABLE testmulticorn add test3 money; SELECT * from testmulticorn where test3 = 12::money; SELECT * from testmulticorn where test1 = '12 €'; +-- Test boolean operation pushdown +ALTER FOREIGN TABLE testmulticorn alter test1 type bool; +select * from testmulticorn where test1; +select * from testmulticorn where NOT test1; +select * from testmulticorn where test1 = TRUE; +select * from testmulticorn where test1 = FALSE; +select * from testmulticorn where test1 IS TRUE; +select * from testmulticorn where test1 IS FALSE; +select * from testmulticorn where test1 IS UNKNOWN; +select * from testmulticorn where test1 IS NOT UNKNOWN; + DROP USER MAPPING FOR current_user SERVER multicorn_srv; DROP EXTENSION multicorn cascade;