/* * BuildDescForRelation * * Given a relation schema (list of ColumnDef nodes), build a TupleDesc. * * Note: the default assumption is no OIDs; caller may modify the returned * TupleDesc if it wants OIDs. Also, tdtypeid will need to be filled in * later on. */ TupleDesc BuildDescForRelation(List *schema) { int natts; AttrNumber attnum; ListCell *l; TupleDesc desc; bool has_not_null; char *attname; Oid atttypid; int32 atttypmod; Oid attcollation; int attdim; /* * allocate a new tuple descriptor */ natts = list_length(schema); desc = CreateTemplateTupleDesc(natts, false); has_not_null = false; attnum = 0; foreach(l, schema) { ColumnDef *entry = lfirst(l); /* * for each entry in the list, get the name and type information from * the list and have TupleDescInitEntry fill in the attribute * information we need. */ attnum++; attname = entry->colname; typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod); attcollation = GetColumnDefCollation(NULL, entry, atttypid); attdim = list_length(entry->typeName->arrayBounds); if (entry->typeName->setof) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("column \"%s\" cannot be declared SETOF", attname))); TupleDescInitEntry(desc, attnum, attname, atttypid, atttypmod, attdim); /* Override TupleDescInitEntry's settings as requested */ TupleDescInitEntryCollation(desc, attnum, attcollation); if (entry->storage) desc->attrs[attnum - 1]->attstorage = entry->storage; /* Fill in additional stuff not handled by TupleDescInitEntry */ desc->attrs[attnum - 1]->attnotnull = entry->is_not_null; has_not_null |= entry->is_not_null; desc->attrs[attnum - 1]->attislocal = entry->is_local; desc->attrs[attnum - 1]->attinhcount = entry->inhcount; }
/* * Given the result tuple descriptor for a function with OUT parameters, * replace any polymorphic columns (ANYELEMENT etc) with correct data types * deduced from the input arguments. Returns TRUE if able to deduce all types, * FALSE if not. */ static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, Node *call_expr) { int natts = tupdesc->natts; int nargs = declared_args->dim1; bool have_anyelement_result = false; bool have_anyarray_result = false; bool have_anynonarray = false; bool have_anyenum = false; Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; Oid anycollation; int i; /* See if there are any polymorphic outputs; quick out if not */ for (i = 0; i < natts; i++) { switch (tupdesc->attrs[i]->atttypid) { case ANYELEMENTOID: have_anyelement_result = true; break; case ANYARRAYOID: have_anyarray_result = true; break; case ANYNONARRAYOID: have_anyelement_result = true; have_anynonarray = true; break; case ANYENUMOID: have_anyelement_result = true; have_anyenum = true; break; default: break; } } if (!have_anyelement_result && !have_anyarray_result) return true; /* * Otherwise, extract actual datatype(s) from input arguments. (We assume * the parser already validated consistency of the arguments.) */ if (!call_expr) return false; /* no hope */ for (i = 0; i < nargs; i++) { switch (declared_args->values[i]) { case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: if (!OidIsValid(anyelement_type)) anyelement_type = get_call_expr_argtype(call_expr, i); break; case ANYARRAYOID: if (!OidIsValid(anyarray_type)) anyarray_type = get_call_expr_argtype(call_expr, i); break; default: break; } } /* If nothing found, parser messed up */ if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type)) return false; /* If needed, deduce one polymorphic type from the other */ if (have_anyelement_result && !OidIsValid(anyelement_type)) anyelement_type = resolve_generic_type(ANYELEMENTOID, anyarray_type, ANYARRAYOID); if (have_anyarray_result && !OidIsValid(anyarray_type)) anyarray_type = resolve_generic_type(ANYARRAYOID, anyelement_type, ANYELEMENTOID); /* Enforce ANYNONARRAY if needed */ if (have_anynonarray && type_is_array(anyelement_type)) return false; /* Enforce ANYENUM if needed */ if (have_anyenum && !type_is_enum(anyelement_type)) return false; /* * Identify the collation to use for polymorphic OUT parameters. (It'll * necessarily be the same for both anyelement and anyarray.) */ anycollation = get_typcollation(OidIsValid(anyelement_type) ? anyelement_type : anyarray_type); if (OidIsValid(anycollation)) { /* * The types are collatable, so consider whether to use a nondefault * collation. We do so if we can identify the input collation used * for the function. */ Oid inputcollation = exprInputCollation(call_expr); if (OidIsValid(inputcollation)) anycollation = inputcollation; } /* And finally replace the tuple column types as needed */ for (i = 0; i < natts; i++) { switch (tupdesc->attrs[i]->atttypid) { case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: TupleDescInitEntry(tupdesc, i + 1, NameStr(tupdesc->attrs[i]->attname), anyelement_type, -1, 0); TupleDescInitEntryCollation(tupdesc, i + 1, anycollation); break; case ANYARRAYOID: TupleDescInitEntry(tupdesc, i + 1, NameStr(tupdesc->attrs[i]->attname), anyarray_type, -1, 0); TupleDescInitEntryCollation(tupdesc, i + 1, anycollation); break; default: break; } } return true; }
/* ---------------------------------------------------------------- * 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; }
/* * initGinState: fill in an empty GinState struct to describe the index * * Note: assorted subsidiary data is allocated in the CurrentMemoryContext. */ void initGinState(GinState *state, Relation index) { TupleDesc origTupdesc = RelationGetDescr(index); int i; MemSet(state, 0, sizeof(GinState)); state->index = index; state->oneCol = (origTupdesc->natts == 1) ? true : false; state->origTupdesc = origTupdesc; for (i = 0; i < origTupdesc->natts; i++) { if (state->oneCol) state->tupdesc[i] = state->origTupdesc; else { state->tupdesc[i] = CreateTemplateTupleDesc(2, false); TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL, INT2OID, -1, 0); TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 2, NULL, origTupdesc->attrs[i]->atttypid, origTupdesc->attrs[i]->atttypmod, origTupdesc->attrs[i]->attndims); TupleDescInitEntryCollation(state->tupdesc[i], (AttrNumber) 2, origTupdesc->attrs[i]->attcollation); } fmgr_info_copy(&(state->compareFn[i]), index_getprocinfo(index, i + 1, GIN_COMPARE_PROC), CurrentMemoryContext); /* * If the index column has a specified collation, index_getprocinfo * will have installed it into the fmgr info, and we should honor it. * However, we may have a collatable storage type for a noncollatable * indexed data type (for instance, hstore uses text index entries). * If there's no index collation then specify default collation in * case the comparison function needs one. This is harmless if the * comparison function doesn't care about collation, so we just do it * unconditionally. (We could alternatively call get_typcollation, * but that seems like expensive overkill --- there aren't going to be * any cases where a GIN storage type has a nondefault collation.) */ if (!OidIsValid(state->compareFn[i].fn_collation)) fmgr_info_set_collation(DEFAULT_COLLATION_OID, &(state->compareFn[i])); fmgr_info_copy(&(state->extractValueFn[i]), index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC), CurrentMemoryContext); fmgr_info_copy(&(state->extractQueryFn[i]), index_getprocinfo(index, i + 1, GIN_EXTRACTQUERY_PROC), CurrentMemoryContext); fmgr_info_copy(&(state->consistentFn[i]), index_getprocinfo(index, i + 1, GIN_CONSISTENT_PROC), CurrentMemoryContext); /* * Check opclass capability to do partial match. */ if (index_getprocid(index, i + 1, GIN_COMPARE_PARTIAL_PROC) != InvalidOid) { fmgr_info_copy(&(state->comparePartialFn[i]), index_getprocinfo(index, i + 1, GIN_COMPARE_PARTIAL_PROC), CurrentMemoryContext); /* As above, install collation spec in case compare fn needs it */ if (!OidIsValid(state->comparePartialFn[i].fn_collation)) fmgr_info_set_collation(DEFAULT_COLLATION_OID, &(state->comparePartialFn[i])); state->canPartialMatch[i] = true; } else { state->canPartialMatch[i] = false; } } }
/* * initGinState: fill in an empty GinState struct to describe the index * * Note: assorted subsidiary data is allocated in the CurrentMemoryContext. */ void initGinState(GinState *state, Relation index) { TupleDesc origTupdesc = RelationGetDescr(index); int i; MemSet(state, 0, sizeof(GinState)); state->index = index; state->oneCol = (origTupdesc->natts == 1) ? true : false; state->origTupdesc = origTupdesc; for (i = 0; i < origTupdesc->natts; i++) { if (state->oneCol) state->tupdesc[i] = state->origTupdesc; else { state->tupdesc[i] = CreateTemplateTupleDesc(2, false); TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL, INT2OID, -1, 0); TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 2, NULL, origTupdesc->attrs[i]->atttypid, origTupdesc->attrs[i]->atttypmod, origTupdesc->attrs[i]->attndims); TupleDescInitEntryCollation(state->tupdesc[i], (AttrNumber) 2, origTupdesc->attrs[i]->attcollation); } /* * If the compare proc isn't specified in the opclass definition, look * up the index key type's default btree comparator. */ if (index_getprocid(index, i + 1, GIN_COMPARE_PROC) != InvalidOid) { fmgr_info_copy(&(state->compareFn[i]), index_getprocinfo(index, i + 1, GIN_COMPARE_PROC), CurrentMemoryContext); } else { TypeCacheEntry *typentry; typentry = lookup_type_cache(origTupdesc->attrs[i]->atttypid, TYPECACHE_CMP_PROC_FINFO); if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not identify a comparison function for type %s", format_type_be(origTupdesc->attrs[i]->atttypid)))); fmgr_info_copy(&(state->compareFn[i]), &(typentry->cmp_proc_finfo), CurrentMemoryContext); } /* Opclass must always provide extract procs */ fmgr_info_copy(&(state->extractValueFn[i]), index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC), CurrentMemoryContext); fmgr_info_copy(&(state->extractQueryFn[i]), index_getprocinfo(index, i + 1, GIN_EXTRACTQUERY_PROC), CurrentMemoryContext); /* * Check opclass capability to do tri-state or binary logic consistent * check. */ if (index_getprocid(index, i + 1, GIN_TRICONSISTENT_PROC) != InvalidOid) { fmgr_info_copy(&(state->triConsistentFn[i]), index_getprocinfo(index, i + 1, GIN_TRICONSISTENT_PROC), CurrentMemoryContext); } if (index_getprocid(index, i + 1, GIN_CONSISTENT_PROC) != InvalidOid) { fmgr_info_copy(&(state->consistentFn[i]), index_getprocinfo(index, i + 1, GIN_CONSISTENT_PROC), CurrentMemoryContext); } if (state->consistentFn[i].fn_oid == InvalidOid && state->triConsistentFn[i].fn_oid == InvalidOid) { elog(ERROR, "missing GIN support function (%d or %d) for attribute %d of index \"%s\"", GIN_CONSISTENT_PROC, GIN_TRICONSISTENT_PROC, i + 1, RelationGetRelationName(index)); } /* * Check opclass capability to do partial match. */ if (index_getprocid(index, i + 1, GIN_COMPARE_PARTIAL_PROC) != InvalidOid) { fmgr_info_copy(&(state->comparePartialFn[i]), index_getprocinfo(index, i + 1, GIN_COMPARE_PARTIAL_PROC), CurrentMemoryContext); state->canPartialMatch[i] = true; } else { state->canPartialMatch[i] = false; } /* * If the index column has a specified collation, we should honor that * while doing comparisons. However, we may have a collatable storage * type for a noncollatable indexed data type (for instance, hstore * uses text index entries). If there's no index collation then * specify default collation in case the support functions need * collation. This is harmless if the support functions don't care * about collation, so we just do it unconditionally. (We could * alternatively call get_typcollation, but that seems like expensive * overkill --- there aren't going to be any cases where a GIN storage * type has a nondefault collation.) */ if (OidIsValid(index->rd_indcollation[i])) state->supportCollation[i] = index->rd_indcollation[i]; else state->supportCollation[i] = DEFAULT_COLLATION_OID; } }