標籤:cat ini 替換 move build check car 情況 orm
這裡把正式指令動詞運算式的部分就整理完了,閱讀的順序例如以下:
一、PostgreSQL程式碼分析,查詢最佳化部分,canonicalize_qual
二、PostgreSQL程式碼分析,查詢最佳化部分,pull_ands()和pull_ors()
三、 PostgreSQL程式碼分析。查詢最佳化部分,process_duplicate_ors
/* * process_duplicate_ors * Given a list of exprs which are ORed together, try to apply * the inverse OR distributive law. * * Returns the resulting expression (could be an AND clause, an OR * clause, or maybe even a single subexpression). *//* * 假設我們有四個表,各自是TEST_A,TEST_B,TEST_C,TEST_D,每一個表有一列, * 也就是TEST_A有一個A列,TEST_B有一個B列,以此類推。 * * 這個函數處理這樣的情況。對於一個選擇,SELECT * FROM TEST_A,TEST_B,TEST_C,TEST_D * WHERE (A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1); * * 語句中的WHERE條件: *(A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1) * 能夠改寫為: *(A=1)AND (B=1 OR C=1 OR D=1) * 這就是這個函數的主要功能。 * * 這個函數的參數是一個list, 對於上述的WHERE條件,orlist的結構例如以下: * orlist中有一個元素,是OR_EXPR類型的BoolExpr,BoolExpr中的結構例如以下:typedef struct BoolExpr{Expr xpr; = 略BoolExprType boolop; = OR_EXPRList *args;= OR中的3個條件,即(A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1)bool plusFlag; = 略} BoolExpr; * * 以下分析函數的詳細實現,大致的步驟為: * 1)分析每一個OR中的公用項。 2)提取公用項, 3)合并剩餘項為AND。 */static Expr *process_duplicate_ors(List *orlist){List *reference = NIL;intnum_subclauses = 0;List *winners;List *neworlist;ListCell *temp;if (orlist == NIL)return NULL;/* probably can‘t happen *//* 假設僅僅有一個。。。
。
。那就算了吧 */if (list_length(orlist) == 1)/* single-expression OR (can this * happen?
) */return linitial(orlist);/* * Choose the shortest AND clause as the reference list --- obviously, any * subclause not in this clause isn‘t in all the clauses. If we find a * clause that‘s not an AND, we can treat it as a one-element AND clause, * which necessarily wins as shortest. *//* * “找最短”。
* 在WHERE語句中: *(A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1) * OR操作串聯了3個子語句。找到當中最短的一個,由於假設有公用項,那麼最短的那個也一定 * 包括公用項。那麼通過找到最短的那個。在後面的操作裡能降低 比較 的次數。
* 在上面的WHERE語句中。3個子語句的長度同樣。依照例如以下運行過程,找到的應該是(A=1 AND B=1), * 即第一個。
*/foreach(temp, orlist){Expr *clause = (Expr *) lfirst(temp);if (and_clause((Node *) clause)){List *subclauses = ((BoolExpr *) clause)->args;intnclauses = list_length(subclauses);/* * 這裡推斷子語句裡的長度,比方對於(A=1 AND B=1)子語句, * 他實際上是一個AND串連起來的兩個 子子語句, 那麼他的長度就是2。 * * 通過nclauses記錄最短的子語句,假設有更短的(nclauses < num_subclauses), * 那麼就替換成最短的。
*/if (reference == NIL || nclauses < num_subclauses){reference = subclauses;num_subclauses = nclauses;}}else{/* * 另一種情況。 就是可能子句不是一個AND語句,這樣看上去不大符合規則, * 那麼把他看做一個總體,那這個就是最短元素。 * ****************************** * 假設代碼運行到這裡。那麼僅僅有兩種情況: * 一種是 ... WHERE (A=1 AND B=1) OR (A=1 AND C=1) OR (A=1)。 * 一種是 ... WHERE ((A=1 OR C=1) AND B=1) OR (A=1 OR C=1). * 假設是這兩種情況,都能夠做例如以下簡化: * 第一種情況簡化為 A=1 * 另外一種情況化簡為 (A=1 OR C=1) * * 第三種情況待補充... */reference = list_make1(clause);break;}}/* * Just in case, eliminate any duplicates in the reference list. *//* 找到最短的。 存到List */reference = list_union(NIL, reference);/* * Check each element of the reference list to see if it‘s in all the OR * clauses. Build a new list of winning clauses. *//* * “找公用項”。 * * NOTE:這時候就能體現“找最短”帶來的優勢。外層迴圈次數會少一些。 * * 假設WHERE語句是: *(A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1) * “找最短”中找到的一定是(A=1 AND B=1)。
* 則外層會有兩次迴圈...(foreach(temp, reference)),兩次迴圈的變數分別為 *A=1 和 B=1。 * 內層有三次迴圈...(foreach(temp2, orlist))。三次迴圈的變數分別為 *(A=1 AND B=1) 和 (A=1 AND C=1) 和 (A=1 AND D=1) * * 示比例如以下: * 假如如今外層迴圈第一次運行,即尋找A=1的公用項,進而假如內層迴圈也是第一次運行, * 即在(A=1 AND B=1)中尋找是否存在A=1這個公用項。發現是存在的(list_member), * 則依次推斷內層迴圈的第二個子句... * * 如上例,詳細來說。這些迴圈分別作的操作是: *外層第一次: *推斷A=1是否在(A=1 AND B=1),在,推斷下一個 *推斷A=1是否在(A=1 AND C=1),在,推斷下一個 *推斷A=1是否在(A=1 AND D=1)。在,A=1是公用項,記錄(winners = lappend...) *外層第二次: *推斷B=1是否在(A=1 AND B=1)。在,推斷下一個 *推斷B=1是否在(A=1 AND C=1),不在,跳出迴圈,下一個不用推斷了。 *推斷B=1是否在(A=1 AND D=1)。未運行,由於上一個不含公用項,就不可能提取了。 */winners = NIL;foreach(temp, reference){Expr *refclause = (Expr *) lfirst(temp);boolwin = true;ListCell *temp2;foreach(temp2, orlist){Expr *clause = (Expr *) lfirst(temp2);if (and_clause((Node *) clause)){if (!list_member(((BoolExpr *) clause)->args, refclause)){win = false;break;}}else{if (!equal(refclause, clause)){win = false;break;}}}if (win)winners = lappend(winners, refclause);}/* * If no winners, we can‘t transform the OR */if (winners == NIL)return make_orclause(orlist);/* * Generate new OR list consisting of the remaining sub-clauses. * * If any clause degenerates to empty, then we have a situation like (A * AND B) OR (A), which can be reduced to just A --- that is, the * additional conditions in other arms of the OR are irrelevant. * * Note that because we use list_difference, any multiple occurrences of a * winning clause in an AND sub-clause will be removed automatically. *//* * “提取公用項”。
* 用list_difference刪除公用項,實現細節不在贅述。 */neworlist = NIL;foreach(temp, orlist){Expr *clause = (Expr *) lfirst(temp);if (and_clause((Node *) clause)){List *subclauses = ((BoolExpr *) clause)->args;/* 看這裡...看這裡..., 消除公用項 */subclauses = list_difference(subclauses, winners);if (subclauses != NIL){/* 消除後,剩餘的拼接起來。拼接成:(B=1 OR C=1 OR D=1)*/if (list_length(subclauses) == 1)neworlist = lappend(neworlist, linitial(subclauses));elseneworlist = lappend(neworlist, make_andclause(subclauses));}else{/* * 這說明子語句中。有一個所有是公用項,也就是例如以下形式: *... WHERE (A=1 AND B=1) OR (A=1) * * 這時候公用項是A=1,第一個子句是(A=1 AND B=1),第二個子句是(A=1), * 第二個子句經過list_difference。返回的結果是NULL。 * 對於這樣的情況,實際上能夠化簡為:A=1,由於(A=1 AND B=1)一定滿足A=1的情況。 */neworlist = NIL;/* degenerate case, see above */break;}}else{if (!list_member(winners, clause))neworlist = lappend(neworlist, clause);else{neworlist = NIL;/* degenerate case, see above */break;}}}/* * Append reduced OR to the winners list, if it‘s not degenerate, handling * the special case of one element correctly (can that really happen?). * Also be careful to maintain AND/OR flatness in case we pulled up a * sub-sub-OR-clause. */if (neworlist != NIL){if (list_length(neworlist) == 1)winners = lappend(winners, linitial(neworlist));else/*neworlist裡面應該是(B=1 OR C=1 OR D=1),所以用make_orclause */winners = lappend(winners, make_orclause(pull_ors(neworlist)));}/* * And return the constructed AND clause, again being wary of a single * element and AND/OR flatness. */if (list_length(winners) == 1)return (Expr *) linitial(winners);else/* 返回的形式是:(A=1)AND (B=1 OR C=1 OR D=1),所以會用make_andclause */return make_andclause(pull_ands(winners));}
PostgreSQL程式碼分析,查詢最佳化部分,process_duplicate_ors