From 26e9675671cdf6142928560dee300f24bbe68ba7 Mon Sep 17 00:00:00 2001
From: JackTan25 <jacktby@gmail.com>
Date: Sat, 26 Nov 2022 05:45:00 +0200
Subject: [PATCH] finish planner modify

---
 src/backend/executor/nodeIndexscan.c    | 292 ++++++++++++++++++++++++--------
 src/backend/optimizer/path/costsize.c   |   9 +
 src/backend/optimizer/path/indxpath.c   |  23 ++-
 src/backend/optimizer/plan/createplan.c |   6 +-
 src/backend/optimizer/plan/planner.c    |  15 ++
 src/backend/utils/cache/lsyscache.c     |  20 +++
 src/include/access/skey.h               |   2 +
 src/include/nodes/pathnodes.h           |   3 +
 src/include/nodes/primnodes.h           |   2 +
 src/include/utils/lsyscache.h           |   2 +-
 10 files changed, 296 insertions(+), 78 deletions(-)

diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 5ef5c69..25bb0c3 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -1207,102 +1207,246 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
 		int			indnkeyatts;
 
 		indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
+		// 注意的修改原因是，我们的查询语句是
+		// select * from data where a <-> {....} < 200; Range Query
+		// select * from data order by a <-> {....} limit 5; Knn Query
+		// 对于Range Query 无法执行的原因是 a <-> {....} < 200 会被解析为
+		// OpExpr,然后继续划分他会这样划分(a <-> {....}) < 200,被解析为了
+		// "indexkey op constant",也就是说他会把((a <-> {....})当作一个
+		// indexkey, 然后检查发现不是就报错了,所以要单独处理
 		if (IsA(clause, OpExpr))
 		{
-			/* indexkey op const or indexkey op expression */
-			int			flags = 0;
-			Datum		scanvalue;
+			if (!IsA((Expr*)get_leftop(clause), OpExpr))//����һ��if else���
+			{
+				/* indexkey op const or indexkey op expression */ // TODO: add the indexkey <-> const op const where search pattern
+				int			flags = 0;
+				Datum		scanvalue;
 
-			opno = ((OpExpr *) clause)->opno;
-			opfuncid = ((OpExpr *) clause)->opfuncid;
+				opno = ((OpExpr*)clause)->opno;
+				opfuncid = ((OpExpr*)clause)->opfuncid;
 
-			/*
-			 * leftop should be the index key Var, possibly relabeled
-			 */
-			leftop = (Expr *) get_leftop(clause);
+				/*
+				 * leftop should be the index key Var, possibly relabeled
+				 */
+				leftop = (Expr*)get_leftop(clause);
 
-			if (leftop && IsA(leftop, RelabelType))
-				leftop = ((RelabelType *) leftop)->arg;
+				if (leftop && IsA(leftop, RelabelType))
+					leftop = ((RelabelType*)leftop)->arg;
 
-			Assert(leftop != NULL);
+				Assert(leftop != NULL);
 
-			if (!(IsA(leftop, Var) &&
-				  ((Var *) leftop)->varno == INDEX_VAR))
-				elog(ERROR, "indexqual doesn't have key on left side");
+				if (!(IsA(leftop, Var) &&
+					((Var*)leftop)->varno == INDEX_VAR))
+					elog(ERROR, "indexqual doesn't have key on left side");
 
-			varattno = ((Var *) leftop)->varattno;
-			if (varattno < 1 || varattno > indnkeyatts)
-				elog(ERROR, "bogus index qualification");
+				varattno = ((Var*)leftop)->varattno;
+				if (varattno < 1 || varattno > indnkeyatts)
+					elog(ERROR, "bogus index qualification");
 
-			/*
-			 * We have to look up the operator's strategy number.  This
-			 * provides a cross-check that the operator does match the index.
-			 */
-			opfamily = index->rd_opfamily[varattno - 1];
+				/*
+				 * We have to look up the operator's strategy number.  This
+				 * provides a cross-check that the operator does match the index.
+				 */
+				opfamily = index->rd_opfamily[varattno - 1];
+				const struct TableAmRoutine* tableam = index->rd_tableam;//���˽ṹ��
+				
+				get_op_opfamily_properties(opno, opfamily, isorderby,
+					&op_strategy,
+					&op_lefttype,
+					&op_righttype);
 
-			get_op_opfamily_properties(opno, opfamily, isorderby,
-									   &op_strategy,
-									   &op_lefttype,
-									   &op_righttype);
+				if (isorderby)
+					flags |= SK_ORDER_BY;
 
-			if (isorderby)
-				flags |= SK_ORDER_BY;
+				/*
+				 * rightop is the constant or variable comparison value
+				 */
+				rightop = (Expr*)get_rightop(clause);
 
-			/*
-			 * rightop is the constant or variable comparison value
-			 */
-			rightop = (Expr *) get_rightop(clause);
+				if (rightop && IsA(rightop, RelabelType))
+					rightop = ((RelabelType*)rightop)->arg;
 
-			if (rightop && IsA(rightop, RelabelType))
-				rightop = ((RelabelType *) rightop)->arg;
+				Assert(rightop != NULL);
 
-			Assert(rightop != NULL);
+				if (IsA(rightop, Const))
+				{
+					/* OK, simple constant comparison value */
+					scanvalue = ((Const*)rightop)->constvalue;
+					if (((Const*)rightop)->constisnull)
+						flags |= SK_ISNULL;
+				}
+				else
+				{
+					/* Need to treat this one as a runtime key */
+					if (n_runtime_keys >= max_runtime_keys)
+					{
+						if (max_runtime_keys == 0)
+						{
+							max_runtime_keys = 8;
+							runtime_keys = (IndexRuntimeKeyInfo*)
+								palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
+						}
+						else
+						{
+							max_runtime_keys *= 2;
+							runtime_keys = (IndexRuntimeKeyInfo*)
+								repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
+						}
+					}
+					runtime_keys[n_runtime_keys].scan_key = this_scan_key;
+					runtime_keys[n_runtime_keys].key_expr =
+						ExecInitExpr(rightop, planstate);
+					runtime_keys[n_runtime_keys].key_toastable =
+						TypeIsToastable(op_righttype);
+					n_runtime_keys++;
+					scanvalue = (Datum)0;
+				}
 
-			if (IsA(rightop, Const))
-			{
-				/* OK, simple constant comparison value */
-				scanvalue = ((Const *) rightop)->constvalue;
-				if (((Const *) rightop)->constisnull)
-					flags |= SK_ISNULL;
+				/*
+				 * initialize the scan key's fields appropriately
+				 */
+				ScanKeyEntryInitialize(this_scan_key,
+					flags,
+					varattno,	/* attribute number to scan */
+					op_strategy, /* op's strategy */
+					op_righttype,	/* strategy subtype */
+					((OpExpr*)clause)->inputcollid,	/* collation */
+					opfuncid,	/* reg proc to use */
+					scanvalue);	/* constant */
+				//=======================================================================================
+				if (((OpExpr*)clause)->KNNValue > 0)
+				{
+					this_scan_key->KNNValues = ((OpExpr*)clause)->KNNValue;
+				}
 			}
-			else
+			else//RangeQuery���������
 			{
-				/* Need to treat this one as a runtime key */
-				if (n_runtime_keys >= max_runtime_keys)
+				/* indexkey op const or indexkey op expression */ // TODO: add the indexkey <-> const op const where search pattern
+				int			flags = 0;
+				Datum		scanvalue;
+				OpExpr* rangeQueryOp;
+				Expr* queryobject;
+				opno = ((OpExpr*)clause)->opno;
+				opfuncid = ((OpExpr*)clause)->opfuncid;
+
+				/*
+				 * leftop should be the index key Var, possibly relabeled
+				 */
+				rangeQueryOp = (OpExpr*)get_leftop(clause);
+				
+				leftop = (Expr*)linitial(rangeQueryOp->args);
+
+				//leftop = (OpExpr*)get_leftop(clause);
+
+				if (leftop && IsA(leftop, RelabelType))
+					leftop = ((RelabelType*)leftop)->arg;
+
+				Assert(leftop != NULL);
+
+				if (!(IsA(leftop, Var) &&
+					((Var*)leftop)->varno == INDEX_VAR))
+					elog(ERROR, "indexqual doesn't have key on left side");
+
+				varattno = ((Var*)leftop)->varattno;
+				if (varattno < 1 || varattno > indnkeyatts)
+					elog(ERROR, "bogus index qualification");
+
+				/*
+				 * We have to look up the operator's strategy number.  This
+				 * provides a cross-check that the operator does match the index.
+				 */
+				opfamily = index->rd_opfamily[varattno - 1];
+				const struct TableAmRoutine* tableam = index->rd_tableam;
+				opno = rangeQueryOp->opno;
+				opfuncid = rangeQueryOp->opfuncid;
+				isorderby = true;
+				get_op_opfamily_properties(opno, opfamily, isorderby,
+					&op_strategy,
+					&op_lefttype,
+					&op_righttype);
+				isorderby = false;
+				if (isorderby)
+					flags |= SK_ORDER_BY;
+
+				/*
+				 * rightop is the constant or variable comparison value
+				 */
+				rightop = (Expr*)get_rightop(clause);
+
+				if (rightop && IsA(rightop, RelabelType))
+					rightop = ((RelabelType*)rightop)->arg;
+
+				Assert(rightop != NULL);
+
+				if (IsA(rightop, Const))
 				{
-					if (max_runtime_keys == 0)
-					{
-						max_runtime_keys = 8;
-						runtime_keys = (IndexRuntimeKeyInfo *)
-							palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
-					}
-					else
+					/* OK, simple constant comparison value */
+					scanvalue = ((Const*)rightop)->constvalue;
+					if (((Const*)rightop)->constisnull)
+						flags |= SK_ISNULL;
+				}
+				else
+				{
+					/* Need to treat this one as a runtime key */
+					if (n_runtime_keys >= max_runtime_keys)
 					{
-						max_runtime_keys *= 2;
-						runtime_keys = (IndexRuntimeKeyInfo *)
-							repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
+						if (max_runtime_keys == 0)
+						{
+							max_runtime_keys = 8;
+							runtime_keys = (IndexRuntimeKeyInfo*)
+								palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
+						}
+						else
+						{
+							max_runtime_keys *= 2;
+							runtime_keys = (IndexRuntimeKeyInfo*)
+								repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
+						}
 					}
+					runtime_keys[n_runtime_keys].scan_key = this_scan_key;
+					runtime_keys[n_runtime_keys].key_expr =
+						ExecInitExpr(rightop, planstate);
+					runtime_keys[n_runtime_keys].key_toastable =
+						TypeIsToastable(op_righttype);
+					n_runtime_keys++;
+					scanvalue = (Datum)0;
 				}
-				runtime_keys[n_runtime_keys].scan_key = this_scan_key;
-				runtime_keys[n_runtime_keys].key_expr =
-					ExecInitExpr(rightop, planstate);
-				runtime_keys[n_runtime_keys].key_toastable =
-					TypeIsToastable(op_righttype);
-				n_runtime_keys++;
-				scanvalue = (Datum) 0;
-			}
 
-			/*
-			 * initialize the scan key's fields appropriately
-			 */
-			ScanKeyEntryInitialize(this_scan_key,
-								   flags,
-								   varattno,	/* attribute number to scan */
-								   op_strategy, /* op's strategy */
-								   op_righttype,	/* strategy subtype */
-								   ((OpExpr *) clause)->inputcollid,	/* collation */
-								   opfuncid,	/* reg proc to use */
-								   scanvalue);	/* constant */
+				// get the range query object
+				queryobject = (Expr*)lsecond(rangeQueryOp->args);
+
+				if (queryobject && IsA(queryobject, RelabelType))
+				{
+					queryobject = ((RelabelType*)queryobject)->arg;
+				}
+
+				Assert(queryobject != NULL);
+
+				if (IsA(queryobject, Const) && !(((Const*)queryobject)->constisnull))
+				{
+					this_scan_key->query = ((Const*)queryobject)->constvalue;
+				}
+				else {
+					elog(ERROR, "object can't be null when do the range query");
+				}
+				/*
+				 * initialize the scan key's fields appropriately
+				 */
+				ScanKeyEntryInitialize(this_scan_key,
+					flags,
+					varattno,	/* attribute number to scan */
+					op_strategy, /* op's strategy */
+					op_righttype,	/* strategy subtype */
+					((OpExpr*)clause)->inputcollid,	/* collation */
+					opfuncid,	/* reg proc to use */
+					scanvalue);	/* constant */
+
+				if (((OpExpr*)clause)->KNNValue > 0)
+				{
+					this_scan_key->KNNValues = ((OpExpr*)clause)->KNNValue;
+				}
+			}
+			//============================================================================================
 		}
 		else if (IsA(clause, RowCompareExpr))
 		{
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 4c6b1d1..77242d1 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -788,6 +788,15 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
 
 	path->path.startup_cost = startup_cost;
 	path->path.total_cost = startup_cost + run_cost;
+
+	//这里李正师兄添加这一块保证在查询的时候强制性的使用索引扫描
+	if (path->indexinfo->relam == 6015 && enable_indexscan && !indexonly)
+	{
+		 // 当可匹配spb时，强制使用spb索引
+		 path->indextotalcost = 0;
+		 path->path.startup_cost = 0;
+		 path->path.total_cost = 0;
+	}
 }
 
 /*
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index c31fcc9..fbcb625 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -2573,7 +2573,28 @@ match_opclause_to_indexcol(PlannerInfo *root,
 											 indexcol,
 											 index);
 	}
-
+	if (leftop && IsA(leftop, OpExpr) && strcmp(get_opname(expr_op),"<") == 0) {
+			OpExpr* opert = (OpExpr*)leftop;
+			Node* left = (Node*)linitial(opert->args);
+			Node* right = (Node*)lsecond(opert->args);
+			expr_op = opert->opno;
+			if (left&&match_index_to_operand(left, indexcol, index) &&
+				!bms_is_member(index_relid, rinfo->right_relids) &&
+				!contain_volatile_functions(right))
+			{
+				char* name = get_am_name_me(index->relam);
+				if (strcmp(name,"spb") == 0)
+				{
+					iclause = makeNode(IndexClause);
+					iclause->rinfo = rinfo;
+					iclause->indexquals = list_make1(rinfo);
+					iclause->lossy = false;
+					iclause->indexcol = indexcol;
+					iclause->indexcols = NIL;
+					return iclause;
+				}
+			} 
+	}
 	return NULL;
 }
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ac86ce9..259e1d6 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -5137,8 +5137,10 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol)
 			result->varno = INDEX_VAR;
 			result->varattno = indexcol + 1;
 			return (Node *) result;
-		}
-		else
+		}else if (IsA(node, OpExpr))
+		{
+			return (Node*)copyObject(node);
+		}else
 			elog(ERROR, "index key does not match expected index column");
 	}
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 5d0fd6e..21e430e 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -3408,6 +3408,21 @@ standard_qp_callback(PlannerInfo *root, void *extra)
 	 * much easier, since we know that the parser ensured that one is a
 	 * superset of the other.
 	 */
+	Node* node = parse->limitCount;
+	int64		count = 0, offset = 0;
+	if (node && IsA(parse->limitCount, Const) && !((Const*)node)->constisnull)
+	{
+		count = DatumGetInt64(((Const*)node)->constvalue);
+	}
+	if (root->sort_pathkeys && count > 0)
+	{
+		ListCell* l;
+		foreach(l, root->sort_pathkeys)
+		{
+			PathKey* key = (PathKey*)lfirst(l);
+			key->limitCount = count;
+		}
+	}
 	if (root->group_pathkeys)
 		root->query_pathkeys = root->group_pathkeys;
 	else if (root->window_pathkeys)
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index a16a63f..994f98d 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3679,3 +3679,23 @@ get_subscription_name(Oid subid, bool missing_ok)
 
 	return subname;
 }
+
+/*
+ * get_am_name - given an access method OID name and type, look up its name.
+ */
+char*
+get_am_name_me(Oid amOid)
+{
+	HeapTuple	tup;
+	char* result = NULL;
+
+	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+	if (HeapTupleIsValid(tup))
+	{
+		Form_pg_am	amform = (Form_pg_am)GETSTRUCT(tup);
+
+		result = pstrdup(NameStr(amform->amname));
+		ReleaseSysCache(tup);
+	}
+	return result;
+}
\ No newline at end of file
diff --git a/src/include/access/skey.h b/src/include/access/skey.h
index b5ab17f..2c3629f 100644
--- a/src/include/access/skey.h
+++ b/src/include/access/skey.h
@@ -70,6 +70,8 @@ typedef struct ScanKeyData
 	Oid			sk_collation;	/* collation to use, if needed */
 	FmgrInfo	sk_func;		/* lookup info for function to call */
 	Datum		sk_argument;	/* data to compare */
+	int			KNNValues;
+	Datum		query;
 } ScanKeyData;
 
 typedef ScanKeyData *ScanKey;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 6bda383..8256e4b 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1370,6 +1370,9 @@ typedef struct PathKey
 	Oid			pk_opfamily;	/* btree opfamily defining the ordering */
 	int			pk_strategy;	/* sort direction (ASC or DESC) */
 	bool		pk_nulls_first; /* do NULLs come before normal values? */
+	
+	int64 limitCount;
+	int64 limitOffset;
 } PathKey;
 
 /*
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4066133..1437ff5 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -667,6 +667,8 @@ typedef struct OpExpr
 
 	/* token location, or -1 if unknown */
 	int			location;
+
+	int			KNNValue;
 } OpExpr;
 
 /*
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 50f0288..a6f77d3 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -202,7 +202,7 @@ extern Oid	get_publication_oid(const char *pubname, bool missing_ok);
 extern char *get_publication_name(Oid pubid, bool missing_ok);
 extern Oid	get_subscription_oid(const char *subname, bool missing_ok);
 extern char *get_subscription_name(Oid subid, bool missing_ok);
-
+extern char* get_am_name_me(Oid amOid);
 #define type_is_array(typid)  (get_element_type(typid) != InvalidOid)
 /* type_is_array_domain accepts both plain arrays and domains over arrays */
 #define type_is_array_domain(typid)  (get_base_element_type(typid) != InvalidOid)
-- 
1.8.3.1

