📄 pl_comp.c
字号:
default: elog(ERROR, "unrecognized ttype: %d", dtype->ttype); result = NULL; /* keep compiler quiet */ break; } return result;}/* * Build a row-variable data structure given the pg_class OID. */static PLpgSQL_row *build_row_from_class(Oid classOid){ PLpgSQL_row *row; Relation rel; Form_pg_class classStruct; const char *relname; int i; /* * Open the relation to get info. */ rel = relation_open(classOid, AccessShareLock); classStruct = RelationGetForm(rel); relname = RelationGetRelationName(rel); /* accept relation, sequence, view, or composite type entries */ if (classStruct->relkind != RELKIND_RELATION && classStruct->relkind != RELKIND_SEQUENCE && classStruct->relkind != RELKIND_VIEW && classStruct->relkind != RELKIND_COMPOSITE_TYPE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("relation \"%s\" is not a table", relname))); /* * Create a row datum entry and all the required variables that it will * point to. */ row = palloc0(sizeof(PLpgSQL_row)); row->dtype = PLPGSQL_DTYPE_ROW; row->rowtupdesc = CreateTupleDescCopy(RelationGetDescr(rel)); row->nfields = classStruct->relnatts; row->fieldnames = palloc(sizeof(char *) * row->nfields); row->varnos = palloc(sizeof(int) * row->nfields); for (i = 0; i < row->nfields; i++) { Form_pg_attribute attrStruct; /* * Get the attribute and check for dropped column */ attrStruct = row->rowtupdesc->attrs[i]; if (!attrStruct->attisdropped) { char *attname; char refname[(NAMEDATALEN * 2) + 100]; PLpgSQL_variable *var; attname = NameStr(attrStruct->attname); snprintf(refname, sizeof(refname), "%s.%s", relname, attname); /* * Create the internal variable for the field * * We know if the table definitions contain a default value or if * the field is declared in the table as NOT NULL. But it's * possible to create a table field as NOT NULL without a default * value and that would lead to problems later when initializing * the variables due to entering a block at execution time. Thus * we ignore this information for now. */ var = plpgsql_build_variable(refname, 0, plpgsql_build_datatype(attrStruct->atttypid, attrStruct->atttypmod), false); /* Add the variable to the row */ row->fieldnames[i] = attname; row->varnos[i] = var->dno; } else { /* Leave a hole in the row structure for the dropped col */ row->fieldnames[i] = NULL; row->varnos[i] = -1; } } relation_close(rel, AccessShareLock); return row;}/* * Build a row-variable data structure given the component variables. */static PLpgSQL_row *build_row_from_vars(PLpgSQL_variable **vars, int numvars){ PLpgSQL_row *row; int i; row = palloc0(sizeof(PLpgSQL_row)); row->dtype = PLPGSQL_DTYPE_ROW; row->rowtupdesc = CreateTemplateTupleDesc(numvars, false); row->nfields = numvars; row->fieldnames = palloc(numvars * sizeof(char *)); row->varnos = palloc(numvars * sizeof(int)); for (i = 0; i < numvars; i++) { PLpgSQL_variable *var = vars[i]; Oid typoid = RECORDOID; int32 typmod = -1; switch (var->dtype) { case PLPGSQL_DTYPE_VAR: typoid = ((PLpgSQL_var *) var)->datatype->typoid; typmod = ((PLpgSQL_var *) var)->datatype->atttypmod; break; case PLPGSQL_DTYPE_REC: break; case PLPGSQL_DTYPE_ROW: if (((PLpgSQL_row *) var)->rowtupdesc) { typoid = ((PLpgSQL_row *) var)->rowtupdesc->tdtypeid; typmod = ((PLpgSQL_row *) var)->rowtupdesc->tdtypmod; } break; default: elog(ERROR, "unrecognized dtype: %d", var->dtype); } row->fieldnames[i] = var->refname; row->varnos[i] = var->dno; TupleDescInitEntry(row->rowtupdesc, i + 1, var->refname, typoid, typmod, 0); } return row;}/* ---------- * plpgsql_parse_datatype Scanner found something that should * be a datatype name. * ---------- */PLpgSQL_type *plpgsql_parse_datatype(const char *string){ Oid type_id; int32 typmod; /* Let the main parser try to parse it under standard SQL rules */ parseTypeString(string, &type_id, &typmod); /* Okay, build a PLpgSQL_type data structure for it */ return plpgsql_build_datatype(type_id, typmod);}/* * plpgsql_build_datatype * Build PLpgSQL_type struct given type OID and typmod. */PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod){ HeapTuple typeTup; PLpgSQL_type *typ; typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(typeOid), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", typeOid); typ = build_datatype(typeTup, typmod); ReleaseSysCache(typeTup); return typ;}/* * Utility subroutine to make a PLpgSQL_type struct given a pg_type entry */static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod){ Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup); PLpgSQL_type *typ; if (!typeStruct->typisdefined) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type \"%s\" is only a shell", NameStr(typeStruct->typname)))); typ = (PLpgSQL_type *) palloc(sizeof(PLpgSQL_type)); typ->typname = pstrdup(NameStr(typeStruct->typname)); typ->typoid = HeapTupleGetOid(typeTup); switch (typeStruct->typtype) { case 'b': /* base type */ case 'd': /* domain */ typ->ttype = PLPGSQL_TTYPE_SCALAR; break; case 'c': /* composite, ie, rowtype */ Assert(OidIsValid(typeStruct->typrelid)); typ->ttype = PLPGSQL_TTYPE_ROW; break; case 'p': /* pseudo */ if (typ->typoid == RECORDOID) typ->ttype = PLPGSQL_TTYPE_REC; else typ->ttype = PLPGSQL_TTYPE_PSEUDO; break; default: elog(ERROR, "unrecognized typtype: %d", (int) typeStruct->typtype); break; } typ->typlen = typeStruct->typlen; typ->typbyval = typeStruct->typbyval; typ->typrelid = typeStruct->typrelid; typ->typioparam = getTypeIOParam(typeTup); fmgr_info(typeStruct->typinput, &(typ->typinput)); typ->atttypmod = typmod; return typ;}/* * plpgsql_parse_err_condition * Generate PLpgSQL_condition entry(s) for an exception condition name * * This has to be able to return a list because there are some duplicate * names in the table of error code names. */PLpgSQL_condition *plpgsql_parse_err_condition(char *condname){ int i; PLpgSQL_condition *new; PLpgSQL_condition *prev; /* * XXX Eventually we will want to look for user-defined exception names * here. */ /* * OTHERS is represented as code 0 (which would map to '00000', but we * have no need to represent that as an exception condition). */ if (strcmp(condname, "others") == 0) { new = palloc(sizeof(PLpgSQL_condition)); new->sqlerrstate = 0; new->condname = condname; new->next = NULL; return new; } prev = NULL; for (i = 0; exception_label_map[i].label != NULL; i++) { if (strcmp(condname, exception_label_map[i].label) == 0) { new = palloc(sizeof(PLpgSQL_condition)); new->sqlerrstate = exception_label_map[i].sqlerrstate; new->condname = condname; new->next = prev; prev = new; } } if (!prev) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unrecognized exception condition \"%s\"", condname))); return prev;}/* ---------- * plpgsql_adddatum Add a variable, record or row * to the compiler's datum list. * ---------- */voidplpgsql_adddatum(PLpgSQL_datum *new){ if (plpgsql_nDatums == datums_alloc) { datums_alloc *= 2; plpgsql_Datums = repalloc(plpgsql_Datums, sizeof(PLpgSQL_datum *) * datums_alloc); } new->dno = plpgsql_nDatums; plpgsql_Datums[plpgsql_nDatums++] = new;}/* ---------- * plpgsql_add_initdatums Put all datum entries created * since the last call into the * finishing code block so the * block knows which variables to * reinitialize when entered. * ---------- */intplpgsql_add_initdatums(int **varnos){ int i; int n = 0; for (i = datums_last; i < plpgsql_nDatums; i++) { switch (plpgsql_Datums[i]->dtype) { case PLPGSQL_DTYPE_VAR: n++; break; default: break; } } if (varnos != NULL) { if (n > 0) { *varnos = (int *) palloc(sizeof(int) * n); n = 0; for (i = datums_last; i < plpgsql_nDatums; i++) { switch (plpgsql_Datums[i]->dtype) { case PLPGSQL_DTYPE_VAR: (*varnos)[n++] = plpgsql_Datums[i]->dno; default: break; } } } else *varnos = NULL; } datums_last = plpgsql_nDatums; return n;}/* * Compute the hashkey for a given function invocation * * The hashkey is returned into the caller-provided storage at *hashkey. */static voidcompute_function_hashkey(FunctionCallInfo fcinfo, Form_pg_proc procStruct, PLpgSQL_func_hashkey *hashkey, bool forValidator){ /* Make sure any unused bytes of the struct are zero */ MemSet(hashkey, 0, sizeof(PLpgSQL_func_hashkey)); /* get function OID */ hashkey->funcOid = fcinfo->flinfo->fn_oid; /* * if trigger, get relation OID. In validation mode we do not know what * relation is intended to be used, so we leave trigrelOid zero; the hash * entry built in this case will never really be used. */ if (CALLED_AS_TRIGGER(fcinfo) && !forValidator) { TriggerData *trigdata = (TriggerData *) fcinfo->context; hashkey->trigrelOid = RelationGetRelid(trigdata->tg_relation); } if (procStruct->pronargs > 0) { /* get the argument types */ memcpy(hashkey->argtypes, procStruct->proargtypes.values, procStruct->pronargs * sizeof(Oid)); /* resolve any polymorphic argument types */ plpgsql_resolve_polymorphic_argtypes(procStruct->pronargs, hashkey->argtypes, NULL, fcinfo->flinfo->fn_expr, forValidator, NameStr(procStruct->proname)); }}/* * This is the same as the standard resolve_polymorphic_argtypes() function, * but with a special case for validation: assume that polymorphic arguments * are integer or integer-array. Also, we go ahead and report the error * if we can't resolve the types. */static voidplpgsql_resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, Node *call_expr, bool forValidator, const char *proname){ int i; if (!forValidator) { /* normal case, pass to standard routine */ if (!resolve_polymorphic_argtypes(numargs, argtypes, argmodes, call_expr)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("could not determine actual argument " "type for polymorphic function \"%s\"", proname))); } else { /* special validation case */ for (i = 0; i < numargs; i++) { switch (argtypes[i]) { case ANYELEMENTOID: argtypes[i] = INT4OID; break; case ANYARRAYOID: argtypes[i] = INT4ARRAYOID; break; default: break; } } }}static voiddelete_function(PLpgSQL_function *func){ /* remove function from hash table */ plpgsql_HashTableDelete(func); /* release the function's storage */ MemoryContextDelete(func->fn_cxt); /* * Caller should be sure not to use passed-in pointer, as it now points to * pfree'd storage */}/* exported so we can call it from plpgsql_init() */voidplpgsql_HashTableInit(void){ HASHCTL ctl; /* don't allow double-initialization */ Assert(plpgsql_HashTable == NULL); memset(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(PLpgSQL_func_hashkey); ctl.entrysize = sizeof(plpgsql_HashEnt); ctl.hash = tag_hash; plpgsql_HashTable = hash_create("PLpgSQL function cache", FUNCS_PER_USER, &ctl, HASH_ELEM | HASH_FUNCTION);}static PLpgSQL_function *plpgsql_HashTableLookup(PLpgSQL_func_hashkey *func_key){ plpgsql_HashEnt *hentry; hentry = (plpgsql_HashEnt *) hash_search(plpgsql_HashTable, (void *) func_key, HASH_FIND, NULL); if (hentry) return hentry->function; else return NULL;}static voidplpgsql_HashTableInsert(PLpgSQL_function *function, PLpgSQL_func_hashkey *func_key){ plpgsql_HashEnt *hentry; bool found; hentry = (plpgsql_HashEnt *) hash_search(plpgsql_HashTable, (void *) func_key, HASH_ENTER, &found); if (found) elog(WARNING, "trying to insert a function that already exists"); hentry->function = function; /* prepare back link from function to hashtable key */ function->fn_hashkey = &hentry->key;}static voidplpgsql_HashTableDelete(PLpgSQL_function *function){ plpgsql_HashEnt *hentry; hentry = (plpgsql_HashEnt *) hash_search(plpgsql_HashTable, (void *) function->fn_hashkey, HASH_REMOVE, NULL); if (hentry == NULL) elog(WARNING, "trying to delete function that does not exist");}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -