analyze.c
来自「PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统」· C语言 代码 · 共 2,222 行 · 第 1/5 页
C
2,222 行
} if (alist_item != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("CREATE VIEW specifies more column " "names than columns"))); } return result;}/* * transformDeleteStmt - * transforms a Delete Statement */static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt){ 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, ACL_DELETE); qry->distinctClause = NIL; /* * The USING clause is non-standard SQL syntax, and is equivalent in * functionality to the FROM list that can be specified for UPDATE. The * USING keyword is used rather than FROM because FROM is already a * keyword in the DELETE syntax. */ transformFromClause(pstate, stmt->usingClause); /* 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); Query *selectQuery = NULL; List *sub_rtable; List *sub_relnamespace; List *sub_varnamespace; List *icolumns; List *attrnos; ListCell *icols; ListCell *attnos; ListCell *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_relnamespace = pstate->p_relnamespace; pstate->p_relnamespace = NIL; sub_varnamespace = pstate->p_varnamespace; pstate->p_varnamespace = NIL; } else { sub_rtable = NIL; /* not used, but keep compiler quiet */ sub_relnamespace = NIL; sub_varnamespace = 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, ACL_INSERT); /* * 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); 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_relnamespace = sub_relnamespace; sub_pstate->p_varnamespace = sub_varnamespace; /* * 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), false); rtr = makeNode(RangeTblRef); /* assume new rte is at end */ rtr->rtindex = list_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 SELECT'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); Expr *expr; if (tle->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, tle->resno, exprType((Node *) tle->expr), exprTypmod((Node *) tle->expr), 0); tle = makeTargetEntry(expr, (AttrNumber) pstate->p_next_resno++, tle->resname, false); qry->targetList = lappend(qry->targetList, tle); } } 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. */ icols = list_head(icolumns); attnos = list_head(attrnos); foreach(tl, qry->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(tl); ResTarget *col; if (icols == NULL || attnos == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("INSERT has more expressions than target columns"))); col = (ResTarget *) lfirst(icols); Assert(IsA(col, ResTarget)); Assert(!tle->resjunk); updateTargetListEntry(pstate, tle, col->name, lfirst_int(attnos), col->indirection); icols = lnext(icols); 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. */ if (stmt->cols != NIL && (icols != NULL || attnos != NULL)) 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;}/* * 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; ListCell *elements; cxt.stmtType = "CREATE TABLE"; cxt.relation = stmt->relation; cxt.inhRelations = stmt->inhRelations; cxt.isalter = false; cxt.columns = NIL; cxt.ckconstraints = NIL; cxt.fkconstraints = NIL; cxt.ixconstraints = NIL; cxt.blist = NIL; cxt.alist = NIL; cxt.pkey = NULL; cxt.hasoids = interpretOidsOption(stmt->hasoids); /* * 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, true, false); /* * Output results. */ q = makeNode(Query); q->commandType = CMD_UTILITY; q->utilityStmt = (Node *) stmt; stmt->tableElts = cxt.columns; stmt->constraints = cxt.ckconstraints; *extras_before = list_concat(*extras_before, cxt.blist); *extras_after = list_concat(cxt.alist, *extras_after); return q;}static voidtransformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt, ColumnDef *column){ bool is_serial; bool saw_nullable; Constraint *constraint; ListCell *clist; cxt->columns = lappend(cxt->columns, column); /* Check for SERIAL pseudo-types */ is_serial = false; if (list_length(column->typename->names) == 1) { char *typname = strVal(linitial(column->typename->names)); if (strcmp(typname, "serial") == 0 || strcmp(typname, "serial4") == 0) { is_serial = true; column->typename->names = NIL; column->typename->typeid = INT4OID; } else if (strcmp(typname, "bigserial") == 0 || strcmp(typname, "serial8") == 0) { is_serial = true; column->typename->names = NIL; column->typename->typeid = INT8OID; } } /* Do necessary work on the column type declaration */ transformColumnType(pstate, column); /* Special actions for SERIAL pseudo-types */ if (is_serial) { Oid snamespaceid; char *snamespace; char *sname; char *qstring; A_Const *snamenode; FuncCall *funccallnode; CreateSeqStmt *seqstmt; /* * Determine namespace and name to use for the sequence. * * Although we use ChooseRelationName, it's not guaranteed that the * selected sequence name won't conflict; given sufficiently long * field names, two different serial columns in the same table could * be assigned the same sequence name, and we'd not notice since we * aren't creating the sequence quite yet. In practice this seems * quite unlikely to be a problem, especially since few people would * need two serial columns in one table. */ snamespaceid = RangeVarGetCreationNamespace(cxt->relation); snamespace = get_namespace_name(snamespaceid); sname = ChooseRelationName(cxt->relation->relname, column->colname, "seq", snamespaceid); ereport(NOTICE, (errmsg("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"", cxt->stmtType, sname, cxt->relation->relname, column->colname))); /* * Build a CREATE SEQUENCE command to create the sequence object, and * add it to the list of things to be done before this CREATE/ALTER * TABLE.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?