v9-0001-Replace-OR-clause-to_ANY.diff.txt

text/plain

Filename: v9-0001-Replace-OR-clause-to_ANY.diff.txt
Type: text/plain
Part: 1
Message: Re: POC, WIP: OR-clause support for indexes
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 46212a77c64..290422005a3 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -102,7 +102,7 @@ static Node *make_nulltest_from_distinct(ParseState *pstate,
 										 A_Expr *distincta, Node *arg);
 typedef struct OrClauseGroupEntry
 {
-	char		   *hash_leftvar_key;
+	int32			hash_leftvar_key;
 
 	Node		   *node;
 	List		   *consts;
@@ -111,63 +111,47 @@ typedef struct OrClauseGroupEntry
 	Node 		   *expr;
 } OrClauseGroupEntry;
 
-static int
-or_name_match(const void *key1, const void *key2, Size keysize)
+/*
+ * TODO: consider different algorithm to manage complexity
+ * in the case of many different clauses,
+ * like Rabin-Karp or Boyer–Moore algorithms.
+ */
+static char *
+clear_patterns(const char *str, const char *start_pattern)
 {
-	const char *name1 = *(const char *const *) key1;
-	const char *name2 = *(const char *const *) key2;
+	int			i = 0;
+	int			j = 0;
+	int			start_pattern_len = strlen(start_pattern);
+	char	   *res = palloc0(sizeof(*res) * (strlen(str) + 1));
 
-	return strcmp(name1, name2);
-}
-
-static uint32
-or_name_hash(const void *key, Size keysize)
-{
-	const char *name = *(const char *const *) key;
+	for (i = 0; str[i];)
+	{
+		if (i >= start_pattern_len && strncmp(&str[i - start_pattern_len],
+											  start_pattern,
+											  start_pattern_len) == 0)
+		{
+			while (str[i] && !(str[i] == '{' || str[i] == '}'))
+				i++;
+		}
+		if (str[i])
+			res[j++] = str[i++];
+	}
 
-	return DatumGetInt32(hash_any((unsigned char *)name, strlen(name)));
+	return res;
 }
 
-static char *
+static int32
 get_key_nconst_node(Node *nconst_node)
 {
-	if (IsA(nconst_node, OpExpr))
-	{
-		OpExpr 	   *clause = (OpExpr*) nconst_node;
-		OpExpr	   *temp = makeNode(OpExpr);
-
-		temp->opno = clause->opno;
-		temp->opfuncid = InvalidOid;
-		temp->opresulttype = clause->opresulttype;
-		temp->opretset = clause->opretset;
-		temp->opcollid = clause->opcollid;
-		temp->inputcollid = clause->inputcollid;
-		temp->location = -1;
-
-		temp->args = list_copy(clause->args);
-		return nodeToString(temp);
-	}
-	else if (IsA(nconst_node, Var))
-	{
-		Var	   *clause = (Var*) nconst_node;
-		Var	   *var = makeNode(Var);
-
-		var->varno = clause->varno;
-		var->varattno = clause->varattno;
-		var->vartype = clause->vartype;
-		var->vartypmod = clause->vartypmod;
-		var->varcollid = clause->varcollid;
-		var->varlevelsup = clause->varlevelsup;
-		var->varattnosyn = clause->varattno;
-		var->location = -1;
-
-		return nodeToString(var);
-	}
-	else
-	{
-		return NULL;
-	}
- }
+	char *str = nodeToString(nconst_node);
+
+	str = clear_patterns(str, " :location");
+
+	if (str == NULL)
+		return 0;
+
+	return DatumGetInt32(hash_any((unsigned char *)str, strlen(str)));
+}
 
 static Node *
 transformBoolExprOr(ParseState *pstate, BoolExpr *expr_orig)
@@ -179,14 +163,12 @@ transformBoolExprOr(ParseState *pstate, BoolExpr *expr_orig)
 	int 			len_ors = list_length(expr_orig->args);
 
 	MemSet(&info, 0, sizeof(info));
-	info.keysize = sizeof(char *);
+	info.keysize = sizeof(int);
 	info.entrysize = sizeof(OrClauseGroupEntry);
-	info.hash = or_name_hash;
-	info.match = or_name_match;
 	or_group_htab = hash_create("OR Groups",
 									  len_ors,
 									  &info,
-									  HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);
+									  HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	/* If this is not an 'OR' expression, skip the transformation */
 	if (expr_orig->boolop != OR_EXPR || !enable_or_transformation || len_ors == 1 || !or_group_htab)
@@ -200,7 +182,7 @@ transformBoolExprOr(ParseState *pstate, BoolExpr *expr_orig)
 		Node			   *nconst_expr = NULL;
 		OrClauseGroupEntry *gentry = NULL;
 		bool				found;
-		char		   	   *str;
+		int32				hash;
 
 		/* At first, transform the arg and evaluate constant expressions. */
 		orqual = transformExprRecurse(pstate, (Node *) arg);
@@ -237,9 +219,9 @@ transformBoolExprOr(ParseState *pstate, BoolExpr *expr_orig)
 			continue;
 		}
 
-		str = get_key_nconst_node(nconst_expr);
+		hash = get_key_nconst_node(nconst_expr);
 
-		if (!op_mergejoinable(((OpExpr *) orqual)->opno, exprType(nconst_expr)) || str == NULL)
+		if (!op_mergejoinable(((OpExpr *) orqual)->opno, exprType(nconst_expr)) || hash == 0)
 		{
 			or_list = lappend(or_list, orqual);
 			continue;
@@ -254,7 +236,7 @@ transformBoolExprOr(ParseState *pstate, BoolExpr *expr_orig)
 		* like a hash table. But also we believe, that the case of many
 		* different variable sides is very rare.
 		*/
-		gentry = hash_search(or_group_htab, &str, HASH_FIND, &found);
+		gentry = hash_search(or_group_htab, &hash, HASH_FIND, &found);
 
 		if (found)
 		{
@@ -267,11 +249,11 @@ transformBoolExprOr(ParseState *pstate, BoolExpr *expr_orig)
 		}
 
 		/* New clause group needed */
-		gentry = hash_search(or_group_htab, &str, HASH_ENTER, &found);
+		gentry = hash_search(or_group_htab, &hash, HASH_ENTER, &found);
 		gentry->node = nconst_expr;
 		gentry->consts = list_make1(const_expr);
 		gentry->expr = orqual;
-		gentry->hash_leftvar_key = str;
+		gentry->hash_leftvar_key = hash;
 	}
 
 	if (or_group_htab && (hash_get_num_entries(or_group_htab) < 1 ||
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index fe815417c13..804fea9f34c 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -702,34 +702,11 @@ explain (costs off) select * from rlp where a = 1 or a = 7;
 (2 rows)
 
 explain (costs off) select * from rlp where a = 1 or b = 'ab';
-                      QUERY PLAN                       
--------------------------------------------------------
- Append
-   ->  Seq Scan on rlp1 rlp_1
-         Filter: (((b)::text = 'ab'::text) OR (a = 1))
-   ->  Seq Scan on rlp2 rlp_2
-         Filter: (((b)::text = 'ab'::text) OR (a = 1))
-   ->  Seq Scan on rlp3abcd rlp_3
-         Filter: (((b)::text = 'ab'::text) OR (a = 1))
-   ->  Seq Scan on rlp4_1 rlp_4
-         Filter: (((b)::text = 'ab'::text) OR (a = 1))
-   ->  Seq Scan on rlp4_2 rlp_5
-         Filter: (((b)::text = 'ab'::text) OR (a = 1))
-   ->  Seq Scan on rlp4_default rlp_6
-         Filter: (((b)::text = 'ab'::text) OR (a = 1))
-   ->  Seq Scan on rlp5_1 rlp_7
-         Filter: (((b)::text = 'ab'::text) OR (a = 1))
-   ->  Seq Scan on rlp5_default rlp_8
-         Filter: (((b)::text = 'ab'::text) OR (a = 1))
-   ->  Seq Scan on rlp_default_10 rlp_9
-         Filter: (((b)::text = 'ab'::text) OR (a = 1))
-   ->  Seq Scan on rlp_default_30 rlp_10
-         Filter: (((b)::text = 'ab'::text) OR (a = 1))
-   ->  Seq Scan on rlp_default_null rlp_11
-         Filter: (((b)::text = 'ab'::text) OR (a = 1))
-   ->  Seq Scan on rlp_default_default rlp_12
-         Filter: (((b)::text = 'ab'::text) OR (a = 1))
-(25 rows)
+        QUERY PLAN        
+--------------------------
+ Result
+   One-Time Filter: false
+(2 rows)
 
 explain (costs off) select * from rlp where a > 20 and a < 27;
                QUERY PLAN                
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 8a380c29a4c..566f51df428 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -134,7 +134,7 @@ select name, setting from pg_settings where name like 'enable%';
  enable_seqscan                 | on
  enable_sort                    | on
  enable_tidscan                 | on
-(22 rows)
+(23 rows)
 
 -- There are always wait event descriptions for various types.
 select type, count(*) > 0 as ok FROM pg_wait_events