static Oid get_function_oid(const char *funcname, const char *argtype, const char *nspname) { #ifndef PROCNAMENSP Oid typid; Oid nspid; Oid funcid; Oid oids[1]; oidvector *oid_v; HeapTuple tup; if (argtype) { typid = TypenameGetTypid(argtype); elog(DEBUG1, "get_function_oid: %s typid: %d", argtype, typid); oids[0] = typid; oid_v = buildoidvector(oids, 1); } else { oid_v = buildoidvector(NULL, 0); } #if !defined(PG_VERSION_NUM) || (PG_VERSION_NUM < 90300) nspid = LookupExplicitNamespace(nspname); #else /* * LookupExplicitNamespace() of PostgreSQL 9.3 or later, has third * argument "missing_ok" which suppresses ERROR exception, but returns * invlaid_oid. See include/catalog/namespace.h */ nspid = LookupExplicitNamespace(nspname, false); #endif elog(DEBUG1, "get_function_oid: oid of \"%s\": %d", nspname, nspid); tup = SearchSysCache(PROCNAMEARGSNSP, PointerGetDatum(funcname), PointerGetDatum(oid_v), ObjectIdGetDatum(nspid), 0); if (HeapTupleIsValid(tup)) { funcid = HeapTupleGetOid(tup); elog(DEBUG1, "get_function_oid: oid of \"%s\": %d", funcname, funcid); ReleaseSysCache(tup); return funcid; } #endif return 0; }
/* * PgxcClassCreate * Create a pgxc_class entry */ void PgxcClassCreate(Oid pcrelid, char pclocatortype, int pcattnum, int pchashalgorithm, int pchashbuckets, int numnodes, Oid *nodes) { Relation pgxcclassrel; HeapTuple htup; bool nulls[Natts_pgxc_class]; Datum values[Natts_pgxc_class]; int i; oidvector *nodes_array; /* Build array of Oids to be inserted */ nodes_array = buildoidvector(nodes, numnodes); /* Iterate through attributes initializing nulls and values */ for (i = 0; i < Natts_pgxc_class; i++) { nulls[i] = false; values[i] = (Datum) 0; } /* should not happen */ if (pcrelid == InvalidOid) { elog(ERROR,"pgxc class relid invalid."); return; } values[Anum_pgxc_class_pcrelid - 1] = ObjectIdGetDatum(pcrelid); values[Anum_pgxc_class_pclocatortype - 1] = CharGetDatum(pclocatortype); if (pclocatortype == LOCATOR_TYPE_HASH || pclocatortype == LOCATOR_TYPE_MODULO) { values[Anum_pgxc_class_pcattnum - 1] = UInt16GetDatum(pcattnum); values[Anum_pgxc_class_pchashalgorithm - 1] = UInt16GetDatum(pchashalgorithm); values[Anum_pgxc_class_pchashbuckets - 1] = UInt16GetDatum(pchashbuckets); } /* Node information */ values[Anum_pgxc_class_nodes - 1] = PointerGetDatum(nodes_array); /* Open the relation for insertion */ pgxcclassrel = heap_open(PgxcClassRelationId, RowExclusiveLock); htup = heap_form_tuple(pgxcclassrel->rd_att, values, nulls); (void) simple_heap_insert(pgxcclassrel, htup); CatalogUpdateIndexes(pgxcclassrel, htup); heap_close(pgxcclassrel, RowExclusiveLock); }
/* --------------------------------------------------------------------- * CREATE PROCEDURAL LANGUAGE * --------------------------------------------------------------------- */ void CreateProceduralLanguage(CreatePLangStmt *stmt) { char *languageName; PLTemplate *pltemplate; Oid handlerOid, valOid; Oid funcrettype; Oid funcargtypes[1]; /* * Check permission */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to create procedural language"))); /* * Translate the language name and check that this language doesn't * already exist */ languageName = case_translate_language_name(stmt->plname); if (SearchSysCacheExists(LANGNAME, PointerGetDatum(languageName), 0, 0, 0)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("language \"%s\" already exists", languageName))); /* * If we have template information for the language, ignore the supplied * parameters (if any) and use the template information. */ if ((pltemplate = find_language_template(languageName)) != NULL) { List *funcname; /* * Give a notice if we are ignoring supplied parameters. */ if (stmt->plhandler) ereport(NOTICE, (errmsg("using pg_pltemplate information instead of CREATE LANGUAGE parameters"))); /* * Find or create the handler function, which we force to be in the * pg_catalog schema. If already present, it must have the correct * return type. */ funcname = SystemFuncName(pltemplate->tmplhandler); handlerOid = LookupFuncName(funcname, 0, funcargtypes, true); if (OidIsValid(handlerOid)) { funcrettype = get_func_rettype(handlerOid); if (funcrettype != LANGUAGE_HANDLEROID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function %s must return type \"language_handler\"", NameListToString(funcname)))); } else { handlerOid = ProcedureCreate(pltemplate->tmplhandler, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ LANGUAGE_HANDLEROID, ClanguageId, F_FMGR_C_VALIDATOR, pltemplate->tmplhandler, pltemplate->tmpllibrary, false, /* isAgg */ false, /* security_definer */ false, /* isStrict */ PROVOLATILE_VOLATILE, buildoidvector(funcargtypes, 0), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL)); } /* * Likewise for the validator, if required; but we don't care about * its return type. */ if (pltemplate->tmplvalidator) { funcname = SystemFuncName(pltemplate->tmplvalidator); funcargtypes[0] = OIDOID; valOid = LookupFuncName(funcname, 1, funcargtypes, true); if (!OidIsValid(valOid)) { valOid = ProcedureCreate(pltemplate->tmplvalidator, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ VOIDOID, ClanguageId, F_FMGR_C_VALIDATOR, pltemplate->tmplvalidator, pltemplate->tmpllibrary, false, /* isAgg */ false, /* security_definer */ false, /* isStrict */ PROVOLATILE_VOLATILE, buildoidvector(funcargtypes, 1), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL)); } } else valOid = InvalidOid; /* ok, create it */ create_proc_lang(languageName, handlerOid, valOid, pltemplate->tmpltrusted); } else { /* * No template, so use the provided information. If there's no * handler clause, the user is trying to rely on a template that we * don't have, so complain accordingly. */ if (!stmt->plhandler) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unsupported language \"%s\"", languageName), errhint("The supported languages are listed in the pg_pltemplate system catalog."))); /* * Lookup the PL handler function and check that it is of the expected * return type */ handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false); funcrettype = get_func_rettype(handlerOid); if (funcrettype != LANGUAGE_HANDLEROID) { /* * We allow OPAQUE just so we can load old dump files. When we * see a handler function declared OPAQUE, change it to * LANGUAGE_HANDLER. (This is probably obsolete and removable?) */ if (funcrettype == OPAQUEOID) { ereport(WARNING, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("changing return type of function %s from \"opaque\" to \"language_handler\"", NameListToString(stmt->plhandler)))); SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID); } else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function %s must return type \"language_handler\"", NameListToString(stmt->plhandler)))); } /* validate the validator function */ if (stmt->plvalidator) { funcargtypes[0] = OIDOID; valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false); /* return value is ignored, so we don't check the type */ } else valOid = InvalidOid; /* ok, create it */ create_proc_lang(languageName, handlerOid, valOid, stmt->pltrusted); } }
/* * Returns pre-defined hidden tuples for pg_proc. */ HeapTuple * GetHiddenPgProcTuples(Relation pg_proc, int *len) { HeapTuple *tuples; Datum values[Natts_pg_proc]; bool nulls[Natts_pg_proc]; MemoryContext oldcontext; static HeapTuple *StaticPgProcTuples = NULL; static int StaticPgProcTupleLen = 0; if (StaticPgProcTuples != NULL) { *len = StaticPgProcTupleLen; return StaticPgProcTuples; } #define N_PGPROC_TUPLES 2 oldcontext = MemoryContextSwitchTo(CacheMemoryContext); tuples = palloc(sizeof(HeapTuple) * N_PGPROC_TUPLES); /* * gp_read_error_log * * CREATE FUNCTION pg_catalog.gp_read_error_log( * text, * cmdtime OUT timestamptz, * relname OUT text, * filename OUT text, * linenum OUT int4, * bytenum OUT int4, * errmsg OUT text, * rawdata OUT text, * rawbytes OUT bytea * ) RETURNS SETOF record AS 'gp_read_error_log' * LANGUAGE internal VOLATILE STRICT <RUN ON SEGMENT>; */ { NameData procname = {"gp_read_error_log"}; Oid proargtypes[] = {TEXTOID}; ArrayType *array; Datum allargtypes[9]; Datum proargmodes[9]; Datum proargnames[9]; MemSet(nulls, false, sizeof(bool) * Natts_pg_proc); values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname); values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(PG_CATALOG_NAMESPACE); values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(BOOTSTRAP_SUPERUSERID); values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(INTERNALlanguageId); values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(false); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(false); values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(true); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(true); values[Anum_pg_proc_provolatile - 1] = CharGetDatum('v'); values[Anum_pg_proc_pronargs - 1] = Int16GetDatum(1); values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(RECORDOID); values[Anum_pg_proc_proiswin - 1] = BoolGetDatum(false); values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(buildoidvector(proargtypes, 1)); allargtypes[0] = TEXTOID; allargtypes[1] = TIMESTAMPTZOID; allargtypes[2] = TEXTOID; allargtypes[3] = TEXTOID; allargtypes[4] = INT4OID; allargtypes[5] = INT4OID; allargtypes[6] = TEXTOID; allargtypes[7] = TEXTOID; allargtypes[8] = BYTEAOID; array = construct_array(allargtypes, 9, OIDOID, 4, true, 'i'); values[Anum_pg_proc_proallargtypes - 1] = PointerGetDatum(array); proargmodes[0] = CharGetDatum('i'); proargmodes[1] = CharGetDatum('o'); proargmodes[2] = CharGetDatum('o'); proargmodes[3] = CharGetDatum('o'); proargmodes[4] = CharGetDatum('o'); proargmodes[5] = CharGetDatum('o'); proargmodes[6] = CharGetDatum('o'); proargmodes[7] = CharGetDatum('o'); proargmodes[8] = CharGetDatum('o'); array = construct_array(proargmodes, 9, CHAROID, 1, true, 'c'); values[Anum_pg_proc_proargmodes - 1] = PointerGetDatum(array); proargnames[0] = CStringGetTextDatum(""); proargnames[1] = CStringGetTextDatum("cmdtime"); proargnames[2] = CStringGetTextDatum("relname"); proargnames[3] = CStringGetTextDatum("filename"); proargnames[4] = CStringGetTextDatum("linenum"); proargnames[5] = CStringGetTextDatum("bytenum"); proargnames[6] = CStringGetTextDatum("errmsg"); proargnames[7] = CStringGetTextDatum("rawdata"); proargnames[8] = CStringGetTextDatum("rawbytes"); array = construct_array(proargnames, 9, TEXTOID, -1, false, 'i'); values[Anum_pg_proc_proargnames - 1] = PointerGetDatum(array); values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum("gp_read_error_log"); values[Anum_pg_proc_probin - 1] = (Datum) 0; nulls[Anum_pg_proc_probin - 1] = true; values[Anum_pg_proc_proacl - 1] = (Datum) 0; nulls[Anum_pg_proc_proacl - 1] = true; /* special data access property, "segment" */ values[Anum_pg_proc_prodataaccess - 1] = CharGetDatum('s'); tuples[0] = heap_form_tuple(RelationGetDescr(pg_proc), values, nulls); ItemPointerSetHidden(&tuples[0]->t_self, F_GP_READ_ERROR_LOG); HeapTupleSetOid(tuples[0], F_GP_READ_ERROR_LOG); } /* * gp_truncate_error_log * * CREATE FUNCTION pg_catalog.gp_truncate_error_log(text) * RETURNS bool AS 'gp_truncate_error_log' * LANGUAGE internal VOLATILE STRICT MODIFIES SQL DATA; */ { NameData procname = {"gp_truncate_error_log"}; Oid proargtypes[] = {TEXTOID}; MemSet(nulls, false, sizeof(bool) * Natts_pg_proc); values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname); values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(PG_CATALOG_NAMESPACE); values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(BOOTSTRAP_SUPERUSERID); values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(INTERNALlanguageId); values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(false); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(false); values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(true); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(false); values[Anum_pg_proc_provolatile - 1] = CharGetDatum('v'); values[Anum_pg_proc_pronargs - 1] = Int16GetDatum(1); values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(BOOLOID); values[Anum_pg_proc_proiswin - 1] = BoolGetDatum(false); values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(buildoidvector(proargtypes, 1)); values[Anum_pg_proc_proallargtypes - 1] = (Datum) 0; nulls[Anum_pg_proc_proallargtypes - 1] = true; values[Anum_pg_proc_proargmodes - 1] = (Datum) 0; nulls[Anum_pg_proc_proargmodes - 1] = true; values[Anum_pg_proc_proargnames - 1] = (Datum) 0; nulls[Anum_pg_proc_proargnames - 1] = true; values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum("gp_truncate_error_log"); values[Anum_pg_proc_probin - 1] = (Datum) 0; nulls[Anum_pg_proc_probin - 1] = true; values[Anum_pg_proc_proacl - 1] = (Datum) 0; nulls[Anum_pg_proc_proacl - 1] = true; values[Anum_pg_proc_prodataaccess - 1] = CharGetDatum('m'); tuples[1] = heap_form_tuple(RelationGetDescr(pg_proc), values, nulls); ItemPointerSetHidden(&tuples[1]->t_self, F_GP_TRUNCATE_ERROR_LOG); HeapTupleSetOid(tuples[1], F_GP_TRUNCATE_ERROR_LOG); } StaticPgProcTuples = tuples; StaticPgProcTupleLen = N_PGPROC_TUPLES; *len = StaticPgProcTupleLen; MemoryContextSwitchTo(oldcontext); return tuples; }
/* * PgxcClassAlter * Modify a pgxc_class entry with given data */ void PgxcClassAlter(Oid pcrelid, char pclocatortype, int pcattnum, int pchashalgorithm, int pchashbuckets, int numnodes, Oid *nodes, PgxcClassAlterType type) { Relation rel; HeapTuple oldtup, newtup; oidvector *nodes_array; Datum new_record[Natts_pgxc_class]; bool new_record_nulls[Natts_pgxc_class]; bool new_record_repl[Natts_pgxc_class]; Assert(OidIsValid(pcrelid)); rel = heap_open(PgxcClassRelationId, RowExclusiveLock); oldtup = SearchSysCacheCopy1(PGXCCLASSRELID, ObjectIdGetDatum(pcrelid)); if (!HeapTupleIsValid(oldtup)) /* should not happen */ elog(ERROR, "cache lookup failed for pgxc_class %u", pcrelid); /* Build array of Oids to be inserted */ nodes_array = buildoidvector(nodes, numnodes); /* Initialize fields */ MemSet(new_record, 0, sizeof(new_record)); MemSet(new_record_nulls, false, sizeof(new_record_nulls)); MemSet(new_record_repl, false, sizeof(new_record_repl)); /* Fields are updated depending on operation type */ switch (type) { case PGXC_CLASS_ALTER_DISTRIBUTION: new_record_repl[Anum_pgxc_class_pclocatortype - 1] = true; new_record_repl[Anum_pgxc_class_pcattnum - 1] = true; new_record_repl[Anum_pgxc_class_pchashalgorithm - 1] = true; new_record_repl[Anum_pgxc_class_pchashbuckets - 1] = true; break; case PGXC_CLASS_ALTER_NODES: new_record_repl[Anum_pgxc_class_nodes - 1] = true; break; case PGXC_CLASS_ALTER_ALL: default: new_record_repl[Anum_pgxc_class_pcrelid - 1] = true; new_record_repl[Anum_pgxc_class_pclocatortype - 1] = true; new_record_repl[Anum_pgxc_class_pcattnum - 1] = true; new_record_repl[Anum_pgxc_class_pchashalgorithm - 1] = true; new_record_repl[Anum_pgxc_class_pchashbuckets - 1] = true; new_record_repl[Anum_pgxc_class_nodes - 1] = true; } /* Set up new fields */ /* Relation Oid */ if (new_record_repl[Anum_pgxc_class_pcrelid - 1]) new_record[Anum_pgxc_class_pcrelid - 1] = ObjectIdGetDatum(pcrelid); /* Locator type */ if (new_record_repl[Anum_pgxc_class_pclocatortype - 1]) new_record[Anum_pgxc_class_pclocatortype - 1] = CharGetDatum(pclocatortype); /* Attribute number of distribution column */ if (new_record_repl[Anum_pgxc_class_pcattnum - 1]) new_record[Anum_pgxc_class_pcattnum - 1] = UInt16GetDatum(pcattnum); /* Hash algorithm type */ if (new_record_repl[Anum_pgxc_class_pchashalgorithm - 1]) new_record[Anum_pgxc_class_pchashalgorithm - 1] = UInt16GetDatum(pchashalgorithm); /* Hash buckets */ if (new_record_repl[Anum_pgxc_class_pchashbuckets - 1]) new_record[Anum_pgxc_class_pchashbuckets - 1] = UInt16GetDatum(pchashbuckets); /* Node information */ if (new_record_repl[Anum_pgxc_class_nodes - 1]) new_record[Anum_pgxc_class_nodes - 1] = PointerGetDatum(nodes_array); /* Update relation */ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), new_record, new_record_nulls, new_record_repl); simple_heap_update(rel, &oldtup->t_self, newtup); CatalogUpdateIndexes(rel, newtup); heap_close(rel, RowExclusiveLock); }
/* * AggregateCreateWithOid */ Oid AggregateCreateWithOid(const char *aggName, Oid aggNamespace, Oid *aggArgTypes, int numArgs, List *aggtransfnName, List *aggprelimfnName, List *aggfinalfnName, List *aggsortopName, Oid aggTransType, const char *agginitval, bool aggordered, Oid procOid) { HeapTuple tup; bool nulls[Natts_pg_aggregate]; Datum values[Natts_pg_aggregate]; Form_pg_proc proc; Oid transfn; Oid invtransfn = InvalidOid; /* MPP windowing optimization */ Oid prelimfn = InvalidOid; /* if omitted, disables MPP 2-stage for this aggregate */ Oid invprelimfn = InvalidOid; /* MPP windowing optimization */ Oid finalfn = InvalidOid; /* can be omitted */ Oid sortop = InvalidOid; /* can be omitted */ bool hasPolyArg; bool hasInternalArg; Oid rettype; Oid finaltype; Oid prelimrettype; Oid *fnArgs; int nargs_transfn; int i; ObjectAddress myself, referenced; cqContext *pcqCtx; cqContext *pcqCtx2; /* sanity checks (caller should have caught these) */ if (!aggName) elog(ERROR, "no aggregate name supplied"); if (!aggtransfnName) elog(ERROR, "aggregate must have a transition function"); /* check for polymorphic arguments and INTERNAL arguments */ hasPolyArg = false; hasInternalArg = false; for (i = 0; i < numArgs; i++) { if (aggArgTypes[i] == ANYARRAYOID || aggArgTypes[i] == ANYELEMENTOID) hasPolyArg = true; else if (aggArgTypes[i] == INTERNALOID) hasInternalArg = true; } /* * If transtype is polymorphic, must have polymorphic argument also; else * we will have no way to deduce the actual transtype. */ if (!hasPolyArg && (aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), errdetail("An aggregate using \"anyarray\" or \"anyelement\" as transition type must have at least one argument of either type."))); /* find the transfn */ nargs_transfn = numArgs + 1; fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid)); fnArgs[0] = aggTransType; memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs, &rettype); elog(DEBUG5,"AggregateCreateWithOid: successfully located transition " "function %s with return type %d", func_signature_string(aggtransfnName, nargs_transfn, fnArgs), rettype); /* * Return type of transfn (possibly after refinement by * enforce_generic_type_consistency, if transtype isn't polymorphic) must * exactly match declared transtype. * * In the non-polymorphic-transtype case, it might be okay to allow a * rettype that's binary-coercible to transtype, but I'm not quite * convinced that it's either safe or useful. When transtype is * polymorphic we *must* demand exact equality. */ if (rettype != aggTransType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of transition function %s is not %s", NameListToString(aggtransfnName), format_type_be(aggTransType)))); pcqCtx2 = caql_beginscan( NULL, cql("SELECT * FROM pg_proc " " WHERE oid = :1 ", ObjectIdGetDatum(transfn))); tup = caql_getnext(pcqCtx2); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for function %u", transfn); proc = (Form_pg_proc) GETSTRUCT(tup); /* * If the transfn is strict and the initval is NULL, make sure first input * type and transtype are the same (or at least binary-compatible), so * that it's OK to use the first input value as the initial transValue. */ if (proc->proisstrict && agginitval == NULL) { if (numArgs < 1 || !IsBinaryCoercible(aggArgTypes[0], aggTransType)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type"))); } caql_endscan(pcqCtx2); /* handle prelimfn, if supplied */ if (aggprelimfnName) { /* * The preliminary state function (pfunc) input arguments are the results of the * state transition function (sfunc) and therefore must be of the same types. */ fnArgs[0] = rettype; fnArgs[1] = rettype; /* * Check that such a function name and prototype exists in the catalog. */ prelimfn = lookup_agg_function(aggprelimfnName, 2, fnArgs, &prelimrettype); elog(DEBUG5,"AggregateCreateWithOid: successfully located preliminary " "function %s with return type %d", func_signature_string(aggprelimfnName, 2, fnArgs), prelimrettype); Assert(OidIsValid(prelimrettype)); /* * The preliminary return type must be of the same type as the internal * state. (See similar error checking for transition types above) */ if (prelimrettype != rettype) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of preliminary function %s is not %s", NameListToString(aggprelimfnName), format_type_be(rettype)))); } /* handle finalfn, if supplied */ if (aggfinalfnName) { fnArgs[0] = aggTransType; finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs, &finaltype); } else { /* * If no finalfn, aggregate result type is type of the state value */ finaltype = aggTransType; } Assert(OidIsValid(finaltype)); /* * If finaltype (i.e. aggregate return type) is polymorphic, inputs must * be polymorphic also, else parser will fail to deduce result type. * (Note: given the previous test on transtype and inputs, this cannot * happen, unless someone has snuck a finalfn definition into the catalogs * that itself violates the rule against polymorphic result with no * polymorphic input.) */ if (!hasPolyArg && (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot determine result data type"), errdetail("An aggregate returning \"anyarray\" or \"anyelement\" " "must have at least one argument of either type."))); /* * Also, the return type can't be INTERNAL unless there's at least one * INTERNAL argument. This is the same type-safety restriction we * enforce for regular functions, but at the level of aggregates. We * must test this explicitly because we allow INTERNAL as the transtype. */ if (finaltype == INTERNALOID && !hasInternalArg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); /* handle sortop, if supplied */ if (aggsortopName) { if (numArgs != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("sort operator can only be specified for single-argument aggregates"))); sortop = LookupOperName(NULL, aggsortopName, aggArgTypes[0], aggArgTypes[0], false, -1); } /* * Everything looks okay. Try to create the pg_proc entry for the * aggregate. (This could fail if there's already a conflicting entry.) */ procOid = ProcedureCreate(aggName, aggNamespace, false, /* no replacement */ false, /* doesn't return a set */ finaltype, /* returnType */ INTERNALlanguageId, /* languageObjectId */ InvalidOid, /* no validator */ InvalidOid, /* no describe function */ "aggregate_dummy", /* placeholder proc */ NULL, /* probin */ true, /* isAgg */ false, /* isWin */ false, /* security invoker (currently not * definable for agg) */ false, /* isStrict (not needed for agg) */ PROVOLATILE_IMMUTABLE, /* volatility (not * needed for agg) */ buildoidvector(aggArgTypes, numArgs), /* paramTypes */ PointerGetDatum(NULL), /* allParamTypes */ PointerGetDatum(NULL), /* parameterModes */ PointerGetDatum(NULL), /* parameterNames */ NIL, /* parameterDefaults */ 1, /* procost */ 0, /* prorows */ PRODATAACCESS_NONE, /* prodataaccess */ procOid); /* * Okay to create the pg_aggregate entry. */ /* initialize nulls and values */ for (i = 0; i < Natts_pg_aggregate; i++) { nulls[i] = false; values[i] = (Datum) 0; } values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid); values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn); values[Anum_pg_aggregate_agginvtransfn - 1] = ObjectIdGetDatum(invtransfn); values[Anum_pg_aggregate_aggprelimfn - 1] = ObjectIdGetDatum(prelimfn); values[Anum_pg_aggregate_agginvprelimfn - 1] = ObjectIdGetDatum(invprelimfn); values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn); values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop); values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType); if (agginitval) values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval); else nulls[Anum_pg_aggregate_agginitval - 1] = true; values[Anum_pg_aggregate_aggordered - 1] = BoolGetDatum(aggordered); pcqCtx = caql_beginscan( NULL, cql("INSERT INTO pg_aggregate", NULL)); tup = caql_form_tuple(pcqCtx, values, nulls); /* insert a new tuple */ caql_insert(pcqCtx, tup); /* implicit update of index as well */ caql_endscan(pcqCtx); /* * Create dependencies for the aggregate (above and beyond those already * made by ProcedureCreate). Note: we don't need an explicit dependency * on aggTransType since we depend on it indirectly through transfn. */ myself.classId = ProcedureRelationId; myself.objectId = procOid; myself.objectSubId = 0; /* Depends on transition function */ referenced.classId = ProcedureRelationId; referenced.objectId = transfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* Depends on inverse transition function, if any */ if (OidIsValid(invtransfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = invtransfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on preliminary aggregation function, if any */ if (OidIsValid(prelimfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = prelimfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on inverse preliminary aggregation function, if any */ if (OidIsValid(invprelimfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = invprelimfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on final function, if any */ if (OidIsValid(finalfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = finalfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on sort operator, if any */ if (OidIsValid(sortop)) { referenced.classId = OperatorRelationId; referenced.objectId = sortop; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } return procOid; }
Relation DirectOpen_Open( DirectOpen *direct, Oid relationId, Oid tablespace, Oid database, Oid relfilenode, FormData_pg_class *pgClass, FormData_pg_attribute *attrArray, FormData_pg_am *pgAm, FormData_pg_index *pgIndex, int2 *indKeyArray, Oid *indClassArray, bool relHasOid) { int natts; int i; Assert(pgClass != NULL); natts = pgClass->relnatts; if (relationId == -1) relationId = pgClass->relfilenode; // Assume it is ok to use the relfilenode as the relationId in our limited usage. if (relfilenode == -1) relfilenode = pgClass->relfilenode; if (!direct->isInit) { /* * Lots of Hard-coded construction of the gp_persistent* RelationS and * dependent objects like tuple descriptors, etc. */ direct->relationData.rd_refcnt = 0; direct->relationData.rd_isvalid = true; direct->relationData.rd_id = relationId; direct->relationData.rd_rel = pgClass; if (pgIndex != NULL) { int pgIndexFixedLen = offsetof(FormData_pg_index, indkey); int indKeyVectorLen = Int2VectorSize(natts); int2vector *indKeyVector; oidvector *indClassVector; uint16 amstrategies; uint16 amsupport; Oid *operator; RegProcedure *support; FmgrInfo *supportinfo; Assert(pgAm != NULL); Assert(indKeyArray != NULL); Assert(indClassArray != NULL); /* * Allocate Formdata_pg_index with fields through indkey * where indkey is a variable length int2vector with indKeyArray values. */ direct->relationData.rd_index = (FormData_pg_index*)palloc( pgIndexFixedLen + indKeyVectorLen); memcpy(direct->relationData.rd_index, pgIndex, pgIndexFixedLen); indKeyVector = buildint2vector( indKeyArray, natts); memcpy( &direct->relationData.rd_index->indkey, indKeyVector, indKeyVectorLen); pfree(indKeyVector); direct->relationData.rd_am = pgAm; amstrategies = pgAm->amstrategies; amsupport = pgAm->amsupport; direct->relationData.rd_indexcxt = TopMemoryContext; /* * Allocate arrays to hold data */ direct->relationData.rd_aminfo = (RelationAmInfo *) MemoryContextAllocZero(TopMemoryContext, sizeof(RelationAmInfo)); direct->relationData.rd_opfamily = (Oid *) MemoryContextAllocZero(TopMemoryContext, natts * sizeof(Oid)); direct->relationData.rd_opcintype = (Oid *) MemoryContextAllocZero(TopMemoryContext, natts * sizeof(Oid)); if (amstrategies > 0) operator = (Oid *) MemoryContextAllocZero(TopMemoryContext, natts * amstrategies * sizeof(Oid)); else operator = NULL; if (amsupport > 0) { int nsupport = natts * amsupport; support = (RegProcedure *) MemoryContextAllocZero(TopMemoryContext, nsupport * sizeof(RegProcedure)); supportinfo = (FmgrInfo *) MemoryContextAllocZero(TopMemoryContext, nsupport * sizeof(FmgrInfo)); } else { support = NULL; supportinfo = NULL; } direct->relationData.rd_operator = operator; direct->relationData.rd_support = support; direct->relationData.rd_supportinfo = supportinfo; direct->relationData.rd_indoption = (int16 *) MemoryContextAllocZero(TopMemoryContext, natts * sizeof(int16)); /* * Create oidvector in rd_indclass with values from indClassArray. */ indClassVector = buildoidvector(indClassArray, natts); /* * Fill the operator and support procedure OID arrays. (aminfo and * supportinfo are left as zeroes, and are filled on-the-fly when used) */ IndexSupportInitialize(indClassVector, operator, support, direct->relationData.rd_opfamily, direct->relationData.rd_opcintype, amstrategies, amsupport, natts); /* * expressions and predicate cache will be filled later. */ direct->relationData.rd_indexprs = NIL; direct->relationData.rd_indpred = NIL; direct->relationData.rd_amcache = NULL; } // Not much in terms of contraints. direct->constrData.has_not_null = true; /* * Setup tuple descriptor for columns. */ direct->descData.natts = pgClass->relnatts; // Make the array of pointers. direct->descData.attrs = (Form_pg_attribute*) MemoryContextAllocZero( TopMemoryContext, sizeof(Form_pg_attribute*) * pgClass->relnatts); for (i = 0; i < pgClass->relnatts; i++) { direct->descData.attrs[i] = (Form_pg_attribute) MemoryContextAllocZero( TopMemoryContext, sizeof(FormData_pg_attribute)); memcpy(direct->descData.attrs[i], &(attrArray[i]), sizeof(FormData_pg_attribute)); // Patch up relation id. direct->descData.attrs[i]->attrelid = relationId; } direct->descData.constr = &direct->constrData; direct->descData.tdtypeid = pgClass->reltype; direct->descData.tdtypmod = -1; direct->descData.tdqdtypmod = -1; direct->descData.tdhasoid = relHasOid; direct->descData.tdrefcount = 1; direct->relationData.rd_att = &direct->descData; direct->pgStat.t_id = relationId; direct->pgStat.t_shared = 1; direct->relationData.pgstat_info = &direct->pgStat; direct->isInit = true; } // UNDONE: Should verify for NON-SHARED relations we don't open relations in different databases / or // UNDONE: open different relations in same database at same time !!! direct->relationData.rd_node.spcNode = tablespace; direct->relationData.rd_node.dbNode = database; direct->relationData.rd_node.relNode = relfilenode; direct->relationData.rd_targblock = InvalidBlockNumber; for (i = 0; i < direct->relationData.rd_rel->relnatts; i++) { Assert(direct->descData.attrs[i] != NULL); // Patch up relation id. direct->descData.attrs[i]->attrelid = direct->relationData.rd_id; } direct->relationData.rd_refcnt++; RelationOpenSmgr(&direct->relationData); return &direct->relationData; }
/* * AggregateCreate */ void AggregateCreate(const char *aggName, Oid aggNamespace, Oid *aggArgTypes, int numArgs, List *aggtransfnName, List *aggfinalfnName, List *aggsortopName, Oid aggTransType, const char *agginitval) { Relation aggdesc; HeapTuple tup; bool nulls[Natts_pg_aggregate]; Datum values[Natts_pg_aggregate]; Form_pg_proc proc; Oid transfn; Oid finalfn = InvalidOid; /* can be omitted */ Oid sortop = InvalidOid; /* can be omitted */ bool hasPolyArg; bool hasInternalArg; Oid rettype; Oid finaltype; Oid *fnArgs; int nargs_transfn; Oid procOid; TupleDesc tupDesc; int i; ObjectAddress myself, referenced; /* sanity checks (caller should have caught these) */ if (!aggName) elog(ERROR, "no aggregate name supplied"); if (!aggtransfnName) elog(ERROR, "aggregate must have a transition function"); /* check for polymorphic and INTERNAL arguments */ hasPolyArg = false; hasInternalArg = false; for (i = 0; i < numArgs; i++) { if (IsPolymorphicType(aggArgTypes[i])) hasPolyArg = true; else if (aggArgTypes[i] == INTERNALOID) hasInternalArg = true; } /* * If transtype is polymorphic, must have polymorphic argument also; else * we will have no way to deduce the actual transtype. */ if (IsPolymorphicType(aggTransType) && !hasPolyArg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument."))); /* find the transfn */ nargs_transfn = numArgs + 1; fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid)); fnArgs[0] = aggTransType; memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs, &rettype); /* * Return type of transfn (possibly after refinement by * enforce_generic_type_consistency, if transtype isn't polymorphic) must * exactly match declared transtype. * * In the non-polymorphic-transtype case, it might be okay to allow a * rettype that's binary-coercible to transtype, but I'm not quite * convinced that it's either safe or useful. When transtype is * polymorphic we *must* demand exact equality. */ if (rettype != aggTransType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of transition function %s is not %s", NameListToString(aggtransfnName), format_type_be(aggTransType)))); tup = SearchSysCache(PROCOID, ObjectIdGetDatum(transfn), 0, 0, 0); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for function %u", transfn); proc = (Form_pg_proc) GETSTRUCT(tup); /* * If the transfn is strict and the initval is NULL, make sure first input * type and transtype are the same (or at least binary-compatible), so * that it's OK to use the first input value as the initial transValue. */ if (proc->proisstrict && agginitval == NULL) { if (numArgs < 1 || !IsBinaryCoercible(aggArgTypes[0], aggTransType)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type"))); } ReleaseSysCache(tup); /* handle finalfn, if supplied */ if (aggfinalfnName) { fnArgs[0] = aggTransType; finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs, &finaltype); } else { /* * If no finalfn, aggregate result type is type of the state value */ finaltype = aggTransType; } Assert(OidIsValid(finaltype)); /* * If finaltype (i.e. aggregate return type) is polymorphic, inputs must * be polymorphic also, else parser will fail to deduce result type. * (Note: given the previous test on transtype and inputs, this cannot * happen, unless someone has snuck a finalfn definition into the catalogs * that itself violates the rule against polymorphic result with no * polymorphic input.) */ if (IsPolymorphicType(finaltype) && !hasPolyArg) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot determine result data type"), errdetail("An aggregate returning a polymorphic type " "must have at least one polymorphic argument."))); /* * Also, the return type can't be INTERNAL unless there's at least one * INTERNAL argument. This is the same type-safety restriction we enforce * for regular functions, but at the level of aggregates. We must test * this explicitly because we allow INTERNAL as the transtype. */ if (finaltype == INTERNALOID && !hasInternalArg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); /* handle sortop, if supplied */ if (aggsortopName) { if (numArgs != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("sort operator can only be specified for single-argument aggregates"))); sortop = LookupOperName(NULL, aggsortopName, aggArgTypes[0], aggArgTypes[0], false, -1); } /* * Everything looks okay. Try to create the pg_proc entry for the * aggregate. (This could fail if there's already a conflicting entry.) */ procOid = ProcedureCreate(aggName, aggNamespace, false, /* no replacement */ false, /* doesn't return a set */ finaltype, /* returnType */ INTERNALlanguageId, /* languageObjectId */ InvalidOid, /* no validator */ "aggregate_dummy", /* placeholder proc */ NULL, /* probin */ true, /* isAgg */ false, /* isWindowFunc */ false, /* security invoker (currently not * definable for agg) */ false, /* isStrict (not needed for agg) */ PROVOLATILE_IMMUTABLE, /* volatility (not * needed for agg) */ buildoidvector(aggArgTypes, numArgs), /* paramTypes */ PointerGetDatum(NULL), /* allParamTypes */ PointerGetDatum(NULL), /* parameterModes */ PointerGetDatum(NULL), /* parameterNames */ NIL, /* parameterDefaults */ PointerGetDatum(NULL), /* proconfig */ 1, /* procost */ 0); /* prorows */ /* * Okay to create the pg_aggregate entry. */ /* initialize nulls and values */ for (i = 0; i < Natts_pg_aggregate; i++) { nulls[i] = false; values[i] = (Datum) NULL; } values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid); values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn); values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn); values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop); values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType); if (agginitval) values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval); else nulls[Anum_pg_aggregate_agginitval - 1] = true; aggdesc = heap_open(AggregateRelationId, RowExclusiveLock); tupDesc = aggdesc->rd_att; tup = heap_form_tuple(tupDesc, values, nulls); simple_heap_insert(aggdesc, tup); CatalogUpdateIndexes(aggdesc, tup); heap_close(aggdesc, RowExclusiveLock); /* * Create dependencies for the aggregate (above and beyond those already * made by ProcedureCreate). Note: we don't need an explicit dependency * on aggTransType since we depend on it indirectly through transfn. */ myself.classId = ProcedureRelationId; myself.objectId = procOid; myself.objectSubId = 0; /* Depends on transition function */ referenced.classId = ProcedureRelationId; referenced.objectId = transfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* Depends on final function, if any */ if (OidIsValid(finalfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = finalfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on sort operator, if any */ if (OidIsValid(sortop)) { referenced.classId = OperatorRelationId; referenced.objectId = sortop; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } }
/* --------------------------------------------------------------------- * CREATE PROCEDURAL LANGUAGE * --------------------------------------------------------------------- */ ObjectAddress CreateProceduralLanguage(CreatePLangStmt *stmt) { PLTemplate *pltemplate; ObjectAddress tmpAddr; Oid handlerOid, inlineOid, valOid; Oid funcrettype; Oid funcargtypes[1]; /* * If we have template information for the language, ignore the supplied * parameters (if any) and use the template information. */ if ((pltemplate = find_language_template(stmt->plname)) != NULL) { List *funcname; /* * Give a notice if we are ignoring supplied parameters. */ if (stmt->plhandler) ereport(NOTICE, (errmsg("using pg_pltemplate information instead of CREATE LANGUAGE parameters"))); /* * Check permission */ if (!superuser()) { if (!pltemplate->tmpldbacreate) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to create procedural language \"%s\"", stmt->plname))); if (!pg_database_ownercheck(MyDatabaseId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); } /* * Find or create the handler function, which we force to be in the * pg_catalog schema. If already present, it must have the correct * return type. */ funcname = SystemFuncName(pltemplate->tmplhandler); handlerOid = LookupFuncName(funcname, 0, funcargtypes, true); if (OidIsValid(handlerOid)) { funcrettype = get_func_rettype(handlerOid); if (funcrettype != LANGUAGE_HANDLEROID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function %s must return type %s", NameListToString(funcname), "language_handler"))); } else { tmpAddr = ProcedureCreate(pltemplate->tmplhandler, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ LANGUAGE_HANDLEROID, BOOTSTRAP_SUPERUSERID, ClanguageId, F_FMGR_C_VALIDATOR, pltemplate->tmplhandler, pltemplate->tmpllibrary, false, /* isAgg */ false, /* isWindowFunc */ false, /* security_definer */ false, /* isLeakProof */ false, /* isStrict */ PROVOLATILE_VOLATILE, PROPARALLEL_UNSAFE, buildoidvector(funcargtypes, 0), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL), NIL, PointerGetDatum(NULL), PointerGetDatum(NULL), 1, 0); handlerOid = tmpAddr.objectId; } /* * Likewise for the anonymous block handler, if required; but we don't * care about its return type. */ if (pltemplate->tmplinline) { funcname = SystemFuncName(pltemplate->tmplinline); funcargtypes[0] = INTERNALOID; inlineOid = LookupFuncName(funcname, 1, funcargtypes, true); if (!OidIsValid(inlineOid)) { tmpAddr = ProcedureCreate(pltemplate->tmplinline, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ VOIDOID, BOOTSTRAP_SUPERUSERID, ClanguageId, F_FMGR_C_VALIDATOR, pltemplate->tmplinline, pltemplate->tmpllibrary, false, /* isAgg */ false, /* isWindowFunc */ false, /* security_definer */ false, /* isLeakProof */ true, /* isStrict */ PROVOLATILE_VOLATILE, PROPARALLEL_UNSAFE, buildoidvector(funcargtypes, 1), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL), NIL, PointerGetDatum(NULL), PointerGetDatum(NULL), 1, 0); inlineOid = tmpAddr.objectId; } } else inlineOid = InvalidOid; /* * Likewise for the validator, if required; but we don't care about * its return type. */ if (pltemplate->tmplvalidator) { funcname = SystemFuncName(pltemplate->tmplvalidator); funcargtypes[0] = OIDOID; valOid = LookupFuncName(funcname, 1, funcargtypes, true); if (!OidIsValid(valOid)) { tmpAddr = ProcedureCreate(pltemplate->tmplvalidator, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ VOIDOID, BOOTSTRAP_SUPERUSERID, ClanguageId, F_FMGR_C_VALIDATOR, pltemplate->tmplvalidator, pltemplate->tmpllibrary, false, /* isAgg */ false, /* isWindowFunc */ false, /* security_definer */ false, /* isLeakProof */ true, /* isStrict */ PROVOLATILE_VOLATILE, PROPARALLEL_UNSAFE, buildoidvector(funcargtypes, 1), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL), NIL, PointerGetDatum(NULL), PointerGetDatum(NULL), 1, 0); valOid = tmpAddr.objectId; } } else valOid = InvalidOid; /* ok, create it */ return create_proc_lang(stmt->plname, stmt->replace, GetUserId(), handlerOid, inlineOid, valOid, pltemplate->tmpltrusted); } else { /* * No template, so use the provided information. If there's no * handler clause, the user is trying to rely on a template that we * don't have, so complain accordingly. */ if (!stmt->plhandler) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unsupported language \"%s\"", stmt->plname), errhint("The supported languages are listed in the pg_pltemplate system catalog."))); /* * Check permission */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to create custom procedural language"))); /* * Lookup the PL handler function and check that it is of the expected * return type */ handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false); funcrettype = get_func_rettype(handlerOid); if (funcrettype != LANGUAGE_HANDLEROID) { /* * We allow OPAQUE just so we can load old dump files. When we * see a handler function declared OPAQUE, change it to * LANGUAGE_HANDLER. (This is probably obsolete and removable?) */ if (funcrettype == OPAQUEOID) { ereport(WARNING, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("changing return type of function %s from \"opaque\" to \"language_handler\"", NameListToString(stmt->plhandler)))); SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID); } else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function %s must return type %s", NameListToString(stmt->plhandler), "language_handler"))); } /* validate the inline function */ if (stmt->plinline) { funcargtypes[0] = INTERNALOID; inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false); /* return value is ignored, so we don't check the type */ } else inlineOid = InvalidOid; /* validate the validator function */ if (stmt->plvalidator) { funcargtypes[0] = OIDOID; valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false); /* return value is ignored, so we don't check the type */ } else valOid = InvalidOid; /* ok, create it */ return create_proc_lang(stmt->plname, stmt->replace, GetUserId(), handlerOid, inlineOid, valOid, stmt->pltrusted); } }
/* --------------------------------------------------------------------- * CREATE PROCEDURAL LANGUAGE * --------------------------------------------------------------------- */ void CreateProceduralLanguage(CreatePLangStmt *stmt) { char *languageName; PLTemplate *pltemplate; Oid handlerOid, inlineOid, valOid; Oid funcrettype; Oid funcargtypes[1]; /* * Translate the language name and check that this language doesn't * already exist */ languageName = case_translate_language_name(stmt->plname); if (SearchSysCacheExists(LANGNAME, PointerGetDatum(languageName), 0, 0, 0)) { /* * MPP-7563: special case plpgsql to omit a notice if it already exists * rather than an error. This allows us to install plpgsql by default * while allowing it to be dropped and not create issues for * dump/restore. This should be phased out in a later releases if/when * plpgsql becomes a true internal language that can not be dropped. * * Note: hardcoding this on the name is semi-safe since we would ignore * any handler functions anyways since plpgsql exists in pg_pltemplate. * Alternatively this logic could be extended to apply to all languages * in pg_pltemplate. */ if (strcmp(languageName, "plpgsql") == 0) { ereport(NOTICE, (errmsg("language \"plpgsql\" already exists, skipping"))); return; } else { ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("language \"%s\" already exists", languageName))); } } /* * If we have template information for the language, ignore the supplied * parameters (if any) and use the template information. */ if ((pltemplate = find_language_template(languageName)) != NULL) { List *funcname; /* * Give a notice if we are ignoring supplied parameters. */ if (stmt->plhandler) if (Gp_role != GP_ROLE_EXECUTE) ereport(NOTICE, (errmsg("using pg_pltemplate information instead of " "CREATE LANGUAGE parameters"))); /* * Check permission */ if (!superuser()) { if (!pltemplate->tmpldbacreate) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to create procedural language \"%s\"", languageName))); if (!pg_database_ownercheck(MyDatabaseId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); } /* * Find or create the handler function, which we force to be in the * pg_catalog schema. If already present, it must have the correct * return type. */ funcname = SystemFuncName(pltemplate->tmplhandler); handlerOid = LookupFuncName(funcname, 0, funcargtypes, true); if (OidIsValid(handlerOid)) { funcrettype = get_func_rettype(handlerOid); if (funcrettype != LANGUAGE_HANDLEROID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function %s must return type \"language_handler\"", NameListToString(funcname)))); } else { handlerOid = ProcedureCreate(pltemplate->tmplhandler, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ LANGUAGE_HANDLEROID, ClanguageId, F_FMGR_C_VALIDATOR, InvalidOid, /* describeFuncOid */ pltemplate->tmplhandler, pltemplate->tmpllibrary, false, /* isAgg */ false, /* isWin */ false, /* security_definer */ false, /* isStrict */ PROVOLATILE_VOLATILE, buildoidvector(funcargtypes, 0), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL), NIL, PointerGetDatum(NULL), 1, 0, PRODATAACCESS_NONE, stmt->plhandlerOid); } /* * Likewise for the anonymous block handler, if required; but we don't * care about its return type. */ if (pltemplate->tmplinline) { funcname = SystemFuncName(pltemplate->tmplinline); funcargtypes[0] = INTERNALOID; inlineOid = LookupFuncName(funcname, 1, funcargtypes, true); if (!OidIsValid(inlineOid)) { inlineOid = ProcedureCreate(pltemplate->tmplinline, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ VOIDOID, ClanguageId, F_FMGR_C_VALIDATOR, InvalidOid, /* describeFuncOid */ pltemplate->tmplinline, pltemplate->tmpllibrary, false, /* isAgg */ false, /* isWin */ false, /* security_definer */ true, /* isStrict */ PROVOLATILE_IMMUTABLE, buildoidvector(funcargtypes, 1), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL), NIL, PointerGetDatum(NULL), 1, 0, PRODATAACCESS_NONE, stmt->plinlineOid); } } else inlineOid = InvalidOid; /* * Likewise for the validator, if required; but we don't care about * its return type. */ if (pltemplate->tmplvalidator) { funcname = SystemFuncName(pltemplate->tmplvalidator); funcargtypes[0] = OIDOID; valOid = LookupFuncName(funcname, 1, funcargtypes, true); if (!OidIsValid(valOid)) { valOid = ProcedureCreate(pltemplate->tmplvalidator, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ VOIDOID, ClanguageId, F_FMGR_C_VALIDATOR, InvalidOid, /* describeFuncOid */ pltemplate->tmplvalidator, pltemplate->tmpllibrary, false, /* isAgg */ false, /* isWin */ false, /* security_definer */ true, /* isStrict */ PROVOLATILE_IMMUTABLE, buildoidvector(funcargtypes, 1), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL), NIL, PointerGetDatum(NULL), 1, 0, PRODATAACCESS_NONE, stmt->plvalidatorOid); } } else valOid = InvalidOid; /* ok, create it */ create_proc_lang(languageName, GetUserId(), handlerOid, inlineOid, valOid, pltemplate->tmpltrusted, &(stmt->plangOid)); } else { /* * No template, so use the provided information. If there's no * handler clause, the user is trying to rely on a template that we * don't have, so complain accordingly. */ if (!stmt->plhandler) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unsupported language \"%s\"", languageName), errhint("The supported languages are listed in the pg_pltemplate system catalog."))); /* * Check permission */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to create custom procedural language"))); /* * Lookup the PL handler function and check that it is of the expected * return type */ handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false); funcrettype = get_func_rettype(handlerOid); if (funcrettype != LANGUAGE_HANDLEROID) { /* * We allow OPAQUE just so we can load old dump files. When we * see a handler function declared OPAQUE, change it to * LANGUAGE_HANDLER. (This is probably obsolete and removable?) */ if (funcrettype == OPAQUEOID) { if (Gp_role != GP_ROLE_EXECUTE) ereport(WARNING, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("changing return type of function %s from \"opaque\" to \"language_handler\"", NameListToString(stmt->plhandler)))); SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID); } else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function %s must return type \"language_handler\"", NameListToString(stmt->plhandler)))); } /* validate the inline function */ if (stmt->plinline) { funcargtypes[0] = INTERNALOID; inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false); /* return value is ignored, so we don't check the type */ } else inlineOid = InvalidOid; /* validate the validator function */ if (stmt->plvalidator) { funcargtypes[0] = OIDOID; valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false); /* return value is ignored, so we don't check the type */ } else valOid = InvalidOid; /* ok, create it */ create_proc_lang(languageName, GetUserId(), handlerOid, inlineOid, valOid, stmt->pltrusted, &(stmt->plangOid)); } if (Gp_role == GP_ROLE_DISPATCH) { stmt->plhandlerOid = handlerOid; stmt->plinlineOid = inlineOid; stmt->plvalidatorOid = valOid; CdbDispatchUtilityStatement((Node *) stmt, DF_CANCEL_ON_ERROR| DF_WITH_SNAPSHOT| DF_NEED_TWO_PHASE, NULL); } }
errmsg("PGXC Node %s: object not defined", node_name))); if (get_pgxc_nodetype(noid) != PGXC_NODE_DATANODE) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("PGXC node %s: only Datanodes can be group members", node_name))); /* OK to pick up Oid of this node */ inTypes[i] = noid; i++; } /* Build array of Oids to be inserted */ nodes_array = buildoidvector(inTypes, member_count); /* Iterate through all attributes initializing nulls and values */ for (i = 0; i < Natts_pgxc_group; i++) { nulls[i] = false; values[i] = (Datum) 0; } /* Insert Data correctly */ values[Anum_pgxc_group_name - 1] = DirectFunctionCall1(namein, CStringGetDatum(group_name)); values[Anum_pgxc_group_members - 1] = PointerGetDatum(nodes_array); /* Open the relation for insertion */ rel = heap_open(PgxcGroupRelationId, RowExclusiveLock);
allTypes[i] = ObjectIdGetDatum(toid); paramModes[i] = CharGetDatum(fp->mode); if (fp->name && fp->name[0]) { paramNames[i] = DirectFunctionCall1(textin, CStringGetDatum(fp->name)); have_names = true; } i++; } /* Now construct the proper outputs as needed */ *parameterTypes = buildoidvector(inTypes, inCount); if (outCount > 0) { *allParameterTypes = construct_array(allTypes, parameterCount, OIDOID, sizeof(Oid), true, 'i'); *parameterModes = construct_array(paramModes, parameterCount, CHAROID, 1, true, 'c'); if (outCount > 1) *requiredResultType = RECORDOID; /* otherwise we set requiredResultType correctly above */ } else { *allParameterTypes = NULL; *parameterModes = NULL;