📄 analyze.c
字号:
/*------------------------------------------------------------------------- * * analyze.c * transform the parse tree into a query tree * * Copyright (c) 1994, Regents of the University of California * * $Id: analyze.c,v 1.110.2.1 1999/08/15 06:50:22 thomas Exp $ * *------------------------------------------------------------------------- */#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <string.h>#include "postgres.h"#include "access/heapam.h"#include "nodes/makefuncs.h"#include "nodes/memnodes.h"#include "nodes/pg_list.h"#include "parser/analyze.h"#include "parser/parse_agg.h"#include "parser/parse_clause.h"#include "parser/parse_node.h"#include "parser/parse_relation.h"#include "parser/parse_target.h"/***S*I***/#include "parser/parse_expr.h"#include "catalog/pg_type.h"#include "parse.h"#include "utils/builtins.h"#include "utils/mcxt.h"static Query *transformStmt(ParseState *pstate, Node *stmt);static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);static Query *transformExtendStmt(ParseState *pstate, ExtendStmt *stmt);static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);static Query *transformCursorStmt(ParseState *pstate, SelectStmt *stmt);static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);static void transformForUpdate(Query *qry, List *forUpdate);void CheckSelectForUpdate(Query *qry);List *extras_before = NIL;List *extras_after = NIL;/* * parse_analyze - * analyze a list of parse trees and transform them if necessary. * * Returns a list of transformed parse trees. Optimizable statements are * all transformed to Query while the rest stays the same. * */List *parse_analyze(List *pl, ParseState *parentParseState){ List *result = NIL; ParseState *pstate; Query *parsetree; while (pl != NIL) { pstate = make_parsestate(parentParseState); parsetree = transformStmt(pstate, lfirst(pl)); if (pstate->p_target_relation != NULL) heap_close(pstate->p_target_relation); while (extras_before != NIL) { result = lappend(result, transformStmt(pstate, lfirst(extras_before))); if (pstate->p_target_relation != NULL) heap_close(pstate->p_target_relation); extras_before = lnext(extras_before); } result = lappend(result, parsetree); while (extras_after != NIL) { result = lappend(result, transformStmt(pstate, lfirst(extras_after))); if (pstate->p_target_relation != NULL) heap_close(pstate->p_target_relation); extras_after = lnext(extras_after); } pl = lnext(pl); pfree(pstate); } return result;}/* * transformStmt - * transform a Parse tree. If it is an optimizable statement, turn it * into a Query tree. */static Query *transformStmt(ParseState *pstate, Node *parseTree){ Query *result = NULL; switch (nodeTag(parseTree)) { /*------------------------ * Non-optimizable statements *------------------------ */ case T_CreateStmt: result = transformCreateStmt(pstate, (CreateStmt *) parseTree); break; case T_IndexStmt: result = transformIndexStmt(pstate, (IndexStmt *) parseTree); break; case T_ExtendStmt: result = transformExtendStmt(pstate, (ExtendStmt *) parseTree); break; case T_RuleStmt: result = transformRuleStmt(pstate, (RuleStmt *) parseTree); break; case T_ViewStmt: { ViewStmt *n = (ViewStmt *) parseTree; n->query = (Query *) transformStmt(pstate, (Node *) n->query); result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) n; } break; case T_VacuumStmt: { MemoryContext oldcontext; /* * make sure that this Query is allocated in TopMemory * context because vacuum spans transactions and we don't * want to lose the vacuum Query due to end-of-transaction * free'ing */ oldcontext = MemoryContextSwitchTo(TopMemoryContext); result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) parseTree; MemoryContextSwitchTo(oldcontext); break; } case T_ExplainStmt: { ExplainStmt *n = (ExplainStmt *) parseTree; result = makeNode(Query); result->commandType = CMD_UTILITY; n->query = transformStmt(pstate, (Node *) n->query); result->utilityStmt = (Node *) parseTree; } break; /*------------------------ * Optimizable statements *------------------------ */ case T_InsertStmt: result = transformInsertStmt(pstate, (InsertStmt *) parseTree); break; case T_DeleteStmt: result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree); break; case T_UpdateStmt: result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree); break; case T_SelectStmt: if (!((SelectStmt *) parseTree)->portalname) { result = transformSelectStmt(pstate, (SelectStmt *) parseTree); result->limitOffset = ((SelectStmt *) parseTree)->limitOffset; result->limitCount = ((SelectStmt *) parseTree)->limitCount; } else result = transformCursorStmt(pstate, (SelectStmt *) parseTree); break; default: /* * other statments don't require any transformation-- just * return the original parsetree, yea! */ result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) parseTree; break; } return result;}/* * transformDeleteStmt - * transforms a Delete Statement */static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt){ Query *qry = makeNode(Query); qry->commandType = CMD_DELETE; /* set up a range table */ makeRangeTable(pstate, stmt->relname, NULL, NULL); qry->uniqueFlag = NULL; /* fix where clause */ qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->rtable = pstate->p_rtable; qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL); qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs) parseCheckAggregates(pstate, qry); return (Query *) qry;}/* * transformInsertStmt - * transform an Insert Statement */static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt){ Query *qry = makeNode(Query); /* make a new query tree */ List *icolumns; qry->commandType = CMD_INSERT; pstate->p_is_insert = true; /* set up a range table */ makeRangeTable(pstate, stmt->relname, stmt->fromClause, NULL); qry->uniqueFlag = stmt->unique; /* fix the target list */ icolumns = pstate->p_insert_columns = makeTargetNames(pstate, stmt->cols); qry->targetList = transformTargetList(pstate, stmt->targetList); /* DEFAULT handling */ if (length(qry->targetList) < pstate->p_target_relation->rd_att->natts && pstate->p_target_relation->rd_att->constr && pstate->p_target_relation->rd_att->constr->num_defval > 0) { Form_pg_attribute *att = pstate->p_target_relation->rd_att->attrs; AttrDefault *defval = pstate->p_target_relation->rd_att->constr->defval; int ndef = pstate->p_target_relation->rd_att->constr->num_defval; /* * if stmt->cols == NIL then makeTargetNames returns list of all * attrs. May have to shorten icolumns list... */ if (stmt->cols == NIL) { List *extrl; int i = length(qry->targetList); foreach(extrl, icolumns) { /* * decrements first, so if we started with zero items it * will now be negative */ if (--i <= 0) break; } /* * this an index into the targetList, so make sure we had one * to start... */ if (i >= 0) { freeList(lnext(extrl)); lnext(extrl) = NIL; } else icolumns = NIL; } while (ndef-- > 0) { List *tl; Ident *id; TargetEntry *te; foreach(tl, icolumns) { id = (Ident *) lfirst(tl); if (namestrcmp(&(att[defval[ndef].adnum - 1]->attname), id->name) == 0) break; } if (tl != NIL) /* something given for this attr */ continue; /* * Nothing given for this attr with DEFAULT expr, so add new * TargetEntry to qry->targetList. Note, that we set resno to * defval[ndef].adnum: it's what * transformTargetList()->make_targetlist_expr() does for * INSERT ... SELECT. But for INSERT ... VALUES * pstate->p_last_resno is used. It doesn't matter for * "normal" using (planner creates proper target list in * preptlist.c), but may break RULEs in some way. It seems * better to create proper target list here... */ te = makeTargetEntry(makeResdom(defval[ndef].adnum, att[defval[ndef].adnum - 1]->atttypid, att[defval[ndef].adnum - 1]->atttypmod, pstrdup(nameout(&(att[defval[ndef].adnum - 1]->attname))), 0, 0, false), (Node *) stringToNode(defval[ndef].adbin)); qry->targetList = lappend(qry->targetList, te); } } /* fix where clause */ qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL); /* * The havingQual has a similar meaning as "qual" in the where * statement. So we can easily use the code from the "where clause" * with some additional traversals done in * .../optimizer/plan/planner.c */ qry->havingQual = transformWhereClause(pstate, stmt->havingClause, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; /* now the range table will not change */ qry->rtable = pstate->p_rtable; qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL); qry->groupClause = transformGroupClause(pstate, stmt->groupClause, qry->targetList); /* fix order clause */ qry->sortClause = transformSortClause(pstate, NIL, NIL, qry->targetList, qry->uniqueFlag); qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs || qry->groupClause) parseCheckAggregates(pstate, qry); /* * The INSERT INTO ... SELECT ... could have a UNION in child, so * unionClause may be false , */ qry->unionall = stmt->unionall; /***S*I***/ /* * Just hand through the unionClause and intersectClause. We will * handle it in the function Except_Intersect_Rewrite() */ qry->unionClause = stmt->unionClause; qry->intersectClause = stmt->intersectClause; /* * If there is a havingQual but there are no aggregates, then there is * something wrong with the query because having must contain * aggregates in its expressions! Otherwise the query could have been * formulated using the where clause. */ if ((qry->hasAggs == false) && (qry->havingQual != NULL)) { elog(ERROR, "SELECT/HAVING requires aggregates to be valid"); return (Query *) NIL; } if (stmt->forUpdate != NULL) transformForUpdate(qry, stmt->forUpdate); return (Query *) qry;}/* * makeObjectName() * * Create a name for an implicitly created index, sequence, constraint, etc. * * The parameters are: the original table name, the original field name, and * a "type" string (such as "seq" or "pkey"). The field name and/or type * can be NULL if not relevant. * * The result is a palloc'd string. * * The basic result we want is "name1_name2_type", omitting "_name2" or * "_type" when those parameters are NULL. However, we must generate * a name with less than NAMEDATALEN characters! So, we truncate one or * both names if necessary to make a short-enough string. The type part * is never truncated (so it had better be reasonably short). * * To reduce the probability of collisions, we might someday add more * smarts to this routine, like including some "hash" characters computed * from the truncated characters. Currently it seems best to keep it simple,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -