analyze.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,258 行 · 第 1/5 页
C
2,258 行
Query *qry = makeNode(Query); Node *qual; qry->commandType = CMD_DELETE; /* set up range table with just the result rel */ qry->resultRelation = setTargetTable(pstate, stmt->relation, interpretInhOption(stmt->relation->inhOpt), true); qry->distinctClause = NIL; /* fix where clause */ qual = transformWhereClause(pstate, stmt->whereClause, "WHERE"); /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs) parseCheckAggregates(pstate, qry); return qry;}/* * transformInsertStmt - * transform an Insert Statement */static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt, List **extras_before, List **extras_after){ Query *qry = makeNode(Query); List *sub_rtable; List *sub_namespace; List *icolumns; List *attrnos; List *attnos; List *tl; qry->commandType = CMD_INSERT; pstate->p_is_insert = true; /* * If a non-nil rangetable/namespace was passed in, and we are doing * INSERT/SELECT, arrange to pass the rangetable/namespace down to the * SELECT. This can only happen if we are inside a CREATE RULE, and * in that case we want the rule's OLD and NEW rtable entries to * appear as part of the SELECT's rtable, not as outer references for * it. (Kluge!) The SELECT's joinlist is not affected however. We * must do this before adding the target table to the INSERT's rtable. */ if (stmt->selectStmt) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; sub_namespace = pstate->p_namespace; pstate->p_namespace = NIL; } else { sub_rtable = NIL; /* not used, but keep compiler quiet */ sub_namespace = NIL; } /* * Must get write lock on INSERT target table before scanning SELECT, * else we will grab the wrong kind of initial lock if the target * table is also mentioned in the SELECT part. Note that the target * table is not added to the joinlist or namespace. */ qry->resultRelation = setTargetTable(pstate, stmt->relation, false, false); /* * Is it INSERT ... SELECT or INSERT ... VALUES? */ if (stmt->selectStmt) { /* * We make the sub-pstate a child of the outer pstate so that it * can see any Param definitions supplied from above. Since the * outer pstate's rtable and namespace are presently empty, there * are no side-effects of exposing names the sub-SELECT shouldn't * be able to see. */ ParseState *sub_pstate = make_parsestate(pstate); Query *selectQuery; RangeTblEntry *rte; RangeTblRef *rtr; /* * Process the source SELECT. * * It is important that this be handled just like a standalone * SELECT; otherwise the behavior of SELECT within INSERT might be * different from a stand-alone SELECT. (Indeed, Postgres up * through 6.5 had bugs of just that nature...) */ sub_pstate->p_rtable = sub_rtable; sub_pstate->p_namespace = sub_namespace; /* * Note: we are not expecting that extras_before and extras_after * are going to be used by the transformation of the SELECT * statement. */ selectQuery = transformStmt(sub_pstate, stmt->selectStmt, extras_before, extras_after); release_pstate_resources(sub_pstate); pfree(sub_pstate); Assert(IsA(selectQuery, Query)); Assert(selectQuery->commandType == CMD_SELECT); if (selectQuery->into) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("INSERT ... SELECT may not specify INTO"))); /* * Make the source be a subquery in the INSERT's rangetable, and * add it to the INSERT's joinlist. */ rte = addRangeTableEntryForSubquery(pstate, selectQuery, makeAlias("*SELECT*", NIL), true); rtr = makeNode(RangeTblRef); /* assume new rte is at end */ rtr->rtindex = length(pstate->p_rtable); Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable)); pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); /* * Generate a targetlist for the INSERT that selects all the * non-resjunk columns from the subquery. (We need this to be * separate from the subquery's tlist because we may add columns, * insert datatype coercions, etc.) * * HACK: unknown-type constants and params in the INSERT's targetlist * are copied up as-is rather than being referenced as subquery * outputs. This is to ensure that when we try to coerce them to * the target column's datatype, the right things happen (see * special cases in coerce_type). Otherwise, this fails: INSERT * INTO foo SELECT 'bar', ... FROM baz */ qry->targetList = NIL; foreach(tl, selectQuery->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(tl); Resdom *resnode = tle->resdom; Expr *expr; if (resnode->resjunk) continue; if (tle->expr && (IsA(tle->expr, Const) ||IsA(tle->expr, Param)) && exprType((Node *) tle->expr) == UNKNOWNOID) expr = tle->expr; else expr = (Expr *) makeVar(rtr->rtindex, resnode->resno, resnode->restype, resnode->restypmod, 0); resnode = copyObject(resnode); resnode->resno = (AttrNumber) pstate->p_next_resno++; qry->targetList = lappend(qry->targetList, makeTargetEntry(resnode, expr)); } } else { /* * For INSERT ... VALUES, transform the given list of values to * form a targetlist for the INSERT. */ qry->targetList = transformTargetList(pstate, stmt->targetList); } /* * Now we are done with SELECT-like processing, and can get on with * transforming the target list to match the INSERT target columns. */ /* Prepare to assign non-conflicting resnos to resjunk attributes */ if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts) pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1; /* Validate stmt->cols list, or build default list if no list given */ icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos); /* * Prepare columns for assignment to target table. */ attnos = attrnos; /* cannot use foreach here because of possible lremove */ tl = qry->targetList; while (tl) { TargetEntry *tle = (TargetEntry *) lfirst(tl); ResTarget *col; /* must advance tl before lremove possibly pfree's it */ tl = lnext(tl); if (icolumns == NIL || attnos == NIL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("INSERT has more expressions than target columns"))); col = (ResTarget *) lfirst(icolumns); Assert(IsA(col, ResTarget)); Assert(!tle->resdom->resjunk); updateTargetListEntry(pstate, tle, col->name, lfirsti(attnos), col->indirection); icolumns = lnext(icolumns); attnos = lnext(attnos); } /* * Ensure that the targetlist has the same number of entries that were * present in the columns list. Don't do the check unless an explicit * columns list was given, though. statements. */ if (stmt->cols != NIL && (icolumns != NIL || attnos != NIL)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("INSERT has more target columns than expressions"))); /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs) parseCheckAggregates(pstate, qry); return 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, * so that the generated names are easily predictable by a person. */char *makeObjectName(char *name1, char *name2, char *typename){ char *name; int overhead = 0; /* chars needed for type and underscores */ int availchars; /* chars available for name(s) */ int name1chars; /* chars allocated to name1 */ int name2chars; /* chars allocated to name2 */ int ndx; name1chars = strlen(name1); if (name2) { name2chars = strlen(name2); overhead++; /* allow for separating underscore */ } else name2chars = 0; if (typename) overhead += strlen(typename) + 1; availchars = NAMEDATALEN - 1 - overhead; /* * If we must truncate, preferentially truncate the longer name. This * logic could be expressed without a loop, but it's simple and * obvious as a loop. */ while (name1chars + name2chars > availchars) { if (name1chars > name2chars) name1chars--; else name2chars--; } if (name1) name1chars = pg_mbcliplen(name1, name1chars, name1chars); if (name2) name2chars = pg_mbcliplen(name2, name2chars, name2chars); /* Now construct the string using the chosen lengths */ name = palloc(name1chars + name2chars + overhead + 1); strncpy(name, name1, name1chars); ndx = name1chars; if (name2) { name[ndx++] = '_'; strncpy(name + ndx, name2, name2chars); ndx += name2chars; } if (typename) { name[ndx++] = '_'; strcpy(name + ndx, typename); } else name[ndx] = '\0'; return name;}static char *CreateIndexName(char *table_name, char *column_name, char *label, List *indices){ int pass = 0; char *iname = NULL; List *ilist; char typename[NAMEDATALEN]; /* * The type name for makeObjectName is label, or labelN if that's * necessary to prevent collisions among multiple indexes for the same * table. Note there is no check for collisions with already-existing * indexes, only among the indexes we're about to create now; this * ought to be improved someday. */ strncpy(typename, label, sizeof(typename)); for (;;) { iname = makeObjectName(table_name, column_name, typename); foreach(ilist, indices) { IndexStmt *index = lfirst(ilist); if (index->idxname != NULL && strcmp(iname, index->idxname) == 0) break; } /* ran through entire list? then no name conflict found so done */ if (ilist == NIL) break; /* found a conflict, so try a new name component */ pfree(iname); snprintf(typename, sizeof(typename), "%s%d", label, ++pass); } return iname;}/* * transformCreateStmt - * transforms the "create table" statement * SQL92 allows constraints to be scattered all over, so thumb through * the columns and collect all constraints into one place. * If there are any implied indices (e.g. UNIQUE or PRIMARY KEY) * then expand those into multiple IndexStmt blocks. * - thomas 1997-12-02 */static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt, List **extras_before, List **extras_after){ CreateStmtContext cxt; Query *q; List *elements; cxt.stmtType = "CREATE TABLE"; cxt.relation = stmt->relation; cxt.inhRelations = stmt->inhRelations; cxt.hasoids = stmt->hasoids; cxt.relOid = InvalidOid; cxt.columns = NIL; cxt.ckconstraints = NIL; cxt.fkconstraints = NIL; cxt.ixconstraints = NIL; cxt.blist = NIL; cxt.alist = NIL; cxt.pkey = NULL; /* * Run through each primary element in the table creation clause. * Separate column defs from constraints, and do preliminary analysis. */ foreach(elements, stmt->tableElts) { Node *element = lfirst(elements); switch (nodeTag(element)) { case T_ColumnDef: transformColumnDefinition(pstate, &cxt, (ColumnDef *) element); break; case T_Constraint: transformTableConstraint(pstate, &cxt, (Constraint *) element); break; case T_FkConstraint: /* No pre-transformation needed */ cxt.fkconstraints = lappend(cxt.fkconstraints, element); break; case T_InhRelation: transformInhRelation(pstate, &cxt, (InhRelation *) element); break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(element)); break; } } Assert(stmt->constraints == NIL); /* * Postprocess constraints that give rise to index definitions. */ transformIndexConstraints(pstate, &cxt); /* * Postprocess foreign-key constraints. */ transformFKConstraints(pstate, &cxt, false);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?