/*--------------------------------------------------------------------- * DefineVirtualRelation * * Create the "view" relation. `DefineRelation' does all the work, * we just provide the correct arguments ... at least when we're * creating a view. If we're updating an existing view, we have to * work harder. *--------------------------------------------------------------------- */ static ObjectAddress DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace, List *options) { Oid viewOid; LOCKMODE lockmode; CreateStmt *createStmt = makeNode(CreateStmt); List *attrList; ListCell *t; /* * create a list of ColumnDef nodes based on the names and types of the * (non-junk) targetlist items from the view's SELECT list. */ attrList = NIL; foreach(t, tlist) { TargetEntry *tle = lfirst(t); if (!tle->resjunk) { ColumnDef *def = makeNode(ColumnDef); def->colname = pstrdup(tle->resname); def->typeName = makeTypeNameFromOid(exprType((Node *) tle->expr), exprTypmod((Node *) tle->expr)); def->inhcount = 0; def->is_local = true; def->is_not_null = false; def->is_from_type = false; def->storage = 0; def->raw_default = NULL; def->cooked_default = NULL; def->collClause = NULL; def->collOid = exprCollation((Node *) tle->expr); def->location = -1; /* * It's possible that the column is of a collatable type but the * collation could not be resolved, so double-check. */ if (type_is_collatable(exprType((Node *) tle->expr))) { if (!OidIsValid(def->collOid)) ereport(ERROR, (errcode(ERRCODE_INDETERMINATE_COLLATION), errmsg("could not determine which collation to use for view column \"%s\"", def->colname), errhint("Use the COLLATE clause to set the collation explicitly."))); } else Assert(!OidIsValid(def->collOid)); def->constraints = NIL; attrList = lappend(attrList, def); } }
/* * makeVarFromTargetEntry - * convenience function to create a same-level Var node from a * TargetEntry */ Var * makeVarFromTargetEntry(Index varno, TargetEntry *tle) { return makeVar(varno, tle->resno, exprType((Node *) tle->expr), exprTypmod((Node *) tle->expr), exprCollation((Node *) tle->expr), 0); }
/* * create_ctas_nodata * * Create CTAS or materialized view when WITH NO DATA is used, starting from * the targetlist of the SELECT or view definition. */ static ObjectAddress create_ctas_nodata(List *tlist, IntoClause *into) { List *attrList; ListCell *t, *lc; /* * Build list of ColumnDefs from non-junk elements of the tlist. If a * column name list was specified in CREATE TABLE AS, override the column * names in the query. (Too few column names are OK, too many are not.) */ attrList = NIL; lc = list_head(into->colNames); foreach(t, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(t); if (!tle->resjunk) { ColumnDef *col; char *colname; if (lc) { colname = strVal(lfirst(lc)); lc = lnext(lc); } else colname = tle->resname; col = makeColumnDef(colname, exprType((Node *) tle->expr), exprTypmod((Node *) tle->expr), exprCollation((Node *) tle->expr)); /* * It's possible that the column is of a collatable type but the * collation could not be resolved, so double-check. (We must * check this here because DefineRelation would adopt the type's * default collation rather than complaining.) */ if (!OidIsValid(col->collOid) && type_is_collatable(col->typeName->typeOid)) ereport(ERROR, (errcode(ERRCODE_INDETERMINATE_COLLATION), errmsg("no collation was derived for column \"%s\" with collatable type %s", col->colname, format_type_be(col->typeName->typeOid)), errhint("Use the COLLATE clause to set the collation explicitly."))); attrList = lappend(attrList, col); } }
/*--------------------------------------------------------------------- * DefineVirtualRelation * * Create a view relation and use the rules system to store the query * for the view. *--------------------------------------------------------------------- */ static ObjectAddress DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace, List *options, Query *viewParse) { Oid viewOid; LOCKMODE lockmode; CreateStmt *createStmt = makeNode(CreateStmt); List *attrList; ListCell *t; /* * create a list of ColumnDef nodes based on the names and types of the * (non-junk) targetlist items from the view's SELECT list. */ attrList = NIL; foreach(t, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(t); if (!tle->resjunk) { ColumnDef *def = makeColumnDef(tle->resname, exprType((Node *) tle->expr), exprTypmod((Node *) tle->expr), exprCollation((Node *) tle->expr)); /* * It's possible that the column is of a collatable type but the * collation could not be resolved, so double-check. */ if (type_is_collatable(exprType((Node *) tle->expr))) { if (!OidIsValid(def->collOid)) ereport(ERROR, (errcode(ERRCODE_INDETERMINATE_COLLATION), errmsg("could not determine which collation to use for view column \"%s\"", def->colname), errhint("Use the COLLATE clause to set the collation explicitly."))); } else Assert(!OidIsValid(def->collOid)); attrList = lappend(attrList, def); } }
/* * makeWholeRowVar - * creates a Var node representing a whole row of the specified RTE * * A whole-row reference is a Var with varno set to the correct range * table entry, and varattno == 0 to signal that it references the whole * tuple. (Use of zero here is unclean, since it could easily be confused * with error cases, but it's not worth changing now.) The vartype indicates * a rowtype; either a named composite type, or RECORD. This function * encapsulates the logic for determining the correct rowtype OID to use. * * If allowScalar is true, then for the case where the RTE is a function * returning a non-composite result type, we produce a normal Var referencing * the function's result directly, instead of the single-column composite * value that the whole-row notation might otherwise suggest. */ Var * makeWholeRowVar(RangeTblEntry *rte, Index varno, Index varlevelsup, bool allowScalar) { Var *result; Oid toid; switch (rte->rtekind) { case RTE_RELATION: /* relation: the rowtype is a named composite type */ toid = get_rel_type_id(rte->relid); if (!OidIsValid(toid)) elog(ERROR, "could not find type OID for relation %u", rte->relid); result = makeVar(varno, InvalidAttrNumber, toid, -1, InvalidOid, varlevelsup); break; case RTE_FUNCTION: toid = exprType(rte->funcexpr); if (type_is_rowtype(toid)) { /* func returns composite; same as relation case */ result = makeVar(varno, InvalidAttrNumber, toid, -1, InvalidOid, varlevelsup); } else if (allowScalar) { /* func returns scalar; just return its output as-is */ result = makeVar(varno, 1, toid, -1, exprCollation(rte->funcexpr), varlevelsup); } else { /* func returns scalar, but we want a composite result */ result = makeVar(varno, InvalidAttrNumber, RECORDOID, -1, InvalidOid, varlevelsup); } break; default: /* * RTE is a join, subselect, or VALUES. We represent this as a * whole-row Var of RECORD type. (Note that in most cases the Var * will be expanded to a RowExpr during planning, but that is not * our concern here.) */ result = makeVar(varno, InvalidAttrNumber, RECORDOID, -1, InvalidOid, varlevelsup); break; } return result; }
/* * makeWholeRowVar - * creates a Var node representing a whole row of the specified RTE * * A whole-row reference is a Var with varno set to the correct range * table entry, and varattno == 0 to signal that it references the whole * tuple. (Use of zero here is unclean, since it could easily be confused * with error cases, but it's not worth changing now.) The vartype indicates * a rowtype; either a named composite type, or RECORD. This function * encapsulates the logic for determining the correct rowtype OID to use. * * If allowScalar is true, then for the case where the RTE is a single function * returning a non-composite result type, we produce a normal Var referencing * the function's result directly, instead of the single-column composite * value that the whole-row notation might otherwise suggest. */ Var * makeWholeRowVar(RangeTblEntry *rte, Index varno, Index varlevelsup, bool allowScalar) { Var *result; Oid toid; Node *fexpr; switch (rte->rtekind) { case RTE_RELATION: /* relation: the rowtype is a named composite type */ toid = get_rel_type_id(rte->relid); if (!OidIsValid(toid)) elog(ERROR, "could not find type OID for relation %u", rte->relid); result = makeVar(varno, InvalidAttrNumber, toid, -1, InvalidOid, varlevelsup); break; case RTE_FUNCTION: /* * If there's more than one function, or ordinality is requested, * force a RECORD result, since there's certainly more than one * column involved and it can't be a known named type. */ if (rte->funcordinality || list_length(rte->functions) != 1) { /* always produces an anonymous RECORD result */ result = makeVar(varno, InvalidAttrNumber, RECORDOID, -1, InvalidOid, varlevelsup); break; } fexpr = ((RangeTblFunction *) linitial(rte->functions))->funcexpr; toid = exprType(fexpr); if (type_is_rowtype(toid)) { /* func returns composite; same as relation case */ result = makeVar(varno, InvalidAttrNumber, toid, -1, InvalidOid, varlevelsup); } else if (allowScalar) { /* func returns scalar; just return its output as-is */ result = makeVar(varno, 1, toid, -1, exprCollation(fexpr), varlevelsup); } else { /* func returns scalar, but we want a composite result */ result = makeVar(varno, InvalidAttrNumber, RECORDOID, -1, InvalidOid, varlevelsup); } break; default: /* * RTE is a join, subselect, or VALUES. We represent this as a * whole-row Var of RECORD type. (Note that in most cases the Var * will be expanded to a RowExpr during planning, but that is not * our concern here.) */ result = makeVar(varno, InvalidAttrNumber, RECORDOID, -1, InvalidOid, varlevelsup); break; } return result; }
/* ---------------------------------------------------------------- * ExecInitFunctionScan * ---------------------------------------------------------------- */ FunctionScanState * ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) { FunctionScanState *scanstate; Oid funcrettype; TypeFuncClass functypclass; TupleDesc tupdesc = NULL; /* check for unsupported flags */ Assert(!(eflags & EXEC_FLAG_MARK)); /* * FunctionScan should not have any children. */ Assert(outerPlan(node) == NULL); Assert(innerPlan(node) == NULL); /* * create new ScanState for node */ scanstate = makeNode(FunctionScanState); scanstate->ss.ps.plan = (Plan *) node; scanstate->ss.ps.state = estate; scanstate->eflags = eflags; /* * Miscellaneous initialization * * create expression context for node */ ExecAssignExprContext(estate, &scanstate->ss.ps); /* * tuple table initialization */ ExecInitResultTupleSlot(estate, &scanstate->ss.ps); ExecInitScanTupleSlot(estate, &scanstate->ss); /* * initialize child expressions */ scanstate->ss.ps.targetlist = (List *) ExecInitExpr((Expr *) node->scan.plan.targetlist, (PlanState *) scanstate); scanstate->ss.ps.qual = (List *) ExecInitExpr((Expr *) node->scan.plan.qual, (PlanState *) scanstate); /* * Now determine if the function returns a simple or composite type, and * build an appropriate tupdesc. */ functypclass = get_expr_result_type(node->funcexpr, &funcrettype, &tupdesc); if (functypclass == TYPEFUNC_COMPOSITE) { /* Composite data type, e.g. a table's row type */ Assert(tupdesc); /* Must copy it out of typcache for safety */ tupdesc = CreateTupleDescCopy(tupdesc); } else if (functypclass == TYPEFUNC_SCALAR) { /* Base data type, i.e. scalar */ char *attname = strVal(linitial(node->funccolnames)); tupdesc = CreateTemplateTupleDesc(1, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, attname, funcrettype, -1, 0); TupleDescInitEntryCollation(tupdesc, (AttrNumber) 1, exprCollation(node->funcexpr)); } else if (functypclass == TYPEFUNC_RECORD) { tupdesc = BuildDescFromLists(node->funccolnames, node->funccoltypes, node->funccoltypmods, node->funccolcollations); } else { /* crummy error message, but parser should have caught this */ elog(ERROR, "function in FROM has unsupported return type"); } /* * For RECORD results, make sure a typmod has been assigned. (The * function should do this for itself, but let's cover things in case it * doesn't.) */ BlessTupleDesc(tupdesc); scanstate->tupdesc = tupdesc; ExecAssignScanType(&scanstate->ss, tupdesc); /* * Other node-specific setup */ scanstate->tuplestorestate = NULL; scanstate->funcexpr = ExecInitExpr((Expr *) node->funcexpr, (PlanState *) scanstate); scanstate->ss.ps.ps_TupFromTlist = false; /* * Initialize result tuple type and projection info. */ ExecAssignResultTypeFromTL(&scanstate->ss.ps); ExecAssignScanProjectionInfo(&scanstate->ss); return scanstate; }