📄 pl_comp.c
字号:
ReleaseSysCache(classtup); pfree(cp[0]); pfree(cp[1]); return T_ERROR; } attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup); typetup = SearchSysCache(TYPEOID, ObjectIdGetDatum(attrStruct->atttypid), 0, 0, 0); if (!HeapTupleIsValid(typetup)) elog(ERROR, "cache lookup failed for type %u", attrStruct->atttypid); /* * Found that - build a compiler type struct and return it */ plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod); ReleaseSysCache(classtup); ReleaseSysCache(attrtup); ReleaseSysCache(typetup); pfree(cp[0]); pfree(cp[1]); return T_DTYPE;}/* ---------- * plpgsql_parse_tripwordtype Same lookup for word.word.word%TYPE * ---------- */#define TYPE_JUNK_LEN 5intplpgsql_parse_tripwordtype(char *word){ Oid classOid; HeapTuple classtup; Form_pg_class classStruct; HeapTuple attrtup; Form_pg_attribute attrStruct; HeapTuple typetup; char *cp[2]; char *colname[1]; int qualified_att_len; int numdots = 0; int i; RangeVar *relvar; /* Do case conversion and word separation */ qualified_att_len = strlen(word) - TYPE_JUNK_LEN; Assert(word[qualified_att_len] == '%'); for (i = 0; i < qualified_att_len; i++) { if (word[i] == '.' && ++numdots == 2) { cp[0] = (char *) palloc((i + 1) * sizeof(char)); memset(cp[0], 0, (i + 1) * sizeof(char)); memcpy(cp[0], word, i * sizeof(char)); /* * qualified_att_len - one based position + 1 (null * terminator) */ cp[1] = (char *) palloc((qualified_att_len - i) * sizeof(char)); memset(cp[1], 0, (qualified_att_len - i) * sizeof(char)); memcpy(cp[1], &word[i + 1], (qualified_att_len - i - 1) * sizeof(char)); break; } } relvar = makeRangeVarFromNameList(stringToQualifiedNameList(cp[0], "plpgsql_parse_tripwordtype")); classOid = RangeVarGetRelid(relvar, true); if (!OidIsValid(classOid)) { pfree(cp[0]); pfree(cp[1]); return T_ERROR; } classtup = SearchSysCache(RELOID, ObjectIdGetDatum(classOid), 0, 0, 0); if (!HeapTupleIsValid(classtup)) { pfree(cp[0]); pfree(cp[1]); return T_ERROR; } /* * It must be a relation, sequence, view, or type */ classStruct = (Form_pg_class) GETSTRUCT(classtup); if (classStruct->relkind != RELKIND_RELATION && classStruct->relkind != RELKIND_SEQUENCE && classStruct->relkind != RELKIND_VIEW && classStruct->relkind != RELKIND_COMPOSITE_TYPE) { ReleaseSysCache(classtup); pfree(cp[0]); pfree(cp[1]); return T_ERROR; } /* * Fetch the named table field and it's type */ plpgsql_convert_ident(cp[1], colname, 1); attrtup = SearchSysCacheAttName(classOid, colname[0]); pfree(colname[0]); if (!HeapTupleIsValid(attrtup)) { ReleaseSysCache(classtup); pfree(cp[0]); pfree(cp[1]); return T_ERROR; } attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup); typetup = SearchSysCache(TYPEOID, ObjectIdGetDatum(attrStruct->atttypid), 0, 0, 0); if (!HeapTupleIsValid(typetup)) elog(ERROR, "cache lookup failed for type %u", attrStruct->atttypid); /* * Found that - build a compiler type struct and return it */ plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod); ReleaseSysCache(classtup); ReleaseSysCache(attrtup); ReleaseSysCache(typetup); pfree(cp[0]); pfree(cp[1]); return T_DTYPE;}/* ---------- * plpgsql_parse_wordrowtype Scanner found word%ROWTYPE. * So word must be a table name. * ---------- */intplpgsql_parse_wordrowtype(char *word){ Oid classOid; char *cp[2]; int i; /* Do case conversion and word separation */ /* We convert %rowtype to .rowtype momentarily to keep converter happy */ i = strlen(word) - 8; Assert(word[i] == '%'); word[i] = '.'; plpgsql_convert_ident(word, cp, 2); word[i] = '%'; /* Lookup the relation */ classOid = RelnameGetRelid(cp[0]); if (!OidIsValid(classOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("relation \"%s\" does not exist", cp[0]))); /* * Build and return the complete row definition */ plpgsql_yylval.row = plpgsql_build_rowtype(classOid); plpgsql_adddatum((PLpgSQL_datum *) plpgsql_yylval.row); pfree(cp[0]); pfree(cp[1]); return T_ROW;}/* ---------- * plpgsql_parse_dblwordrowtype Scanner found word.word%ROWTYPE. * So word must be namespace qualified a table name. * ---------- */#define ROWTYPE_JUNK_LEN 8intplpgsql_parse_dblwordrowtype(char *word){ Oid classOid; char *cp; int i; RangeVar *relvar; /* Do case conversion and word separation */ /* We convert %rowtype to .rowtype momentarily to keep converter happy */ i = strlen(word) - ROWTYPE_JUNK_LEN; Assert(word[i] == '%'); cp = (char *) palloc((i + 1) * sizeof(char)); memset(cp, 0, (i + 1) * sizeof(char)); memcpy(cp, word, i * sizeof(char)); /* Lookup the relation */ relvar = makeRangeVarFromNameList(stringToQualifiedNameList(cp, "plpgsql_parse_dblwordrowtype")); classOid = RangeVarGetRelid(relvar, true); if (!OidIsValid(classOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("relation \"%s\" does not exist", cp))); /* * Build and return the complete row definition */ plpgsql_yylval.row = plpgsql_build_rowtype(classOid); plpgsql_adddatum((PLpgSQL_datum *) plpgsql_yylval.row); pfree(cp); return T_ROW;}/* * Build a rowtype data structure given the pg_class OID. */PLpgSQL_row *plpgsql_build_rowtype(Oid classOid){ PLpgSQL_row *row; Relation rel; Form_pg_class classStruct; const char *relname; int i; MemoryContext oldcxt; /* * 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 = malloc(sizeof(PLpgSQL_row)); memset(row, 0, sizeof(PLpgSQL_row)); row->dtype = PLPGSQL_DTYPE_ROW; /* * This is a bit ugly --- need a permanent copy of the rel's tupdesc. * Someday all these mallocs should go away in favor of a per-function * memory context ... */ oldcxt = MemoryContextSwitchTo(TopMemoryContext); row->rowtupdesc = CreateTupleDescCopy(RelationGetDescr(rel)); MemoryContextSwitchTo(oldcxt); row->nfields = classStruct->relnatts; row->fieldnames = malloc(sizeof(char *) * row->nfields); row->varnos = malloc(sizeof(int) * row->nfields); for (i = 0; i < row->nfields; i++) { Form_pg_attribute attrStruct; /* * Get the attribute and check for dropped column */ attrStruct = RelationGetDescr(rel)->attrs[i]; if (!attrStruct->attisdropped) { const char *attname; HeapTuple typetup; PLpgSQL_var *var; attname = NameStr(attrStruct->attname); typetup = SearchSysCache(TYPEOID, ObjectIdGetDatum(attrStruct->atttypid), 0, 0, 0); if (!HeapTupleIsValid(typetup)) elog(ERROR, "cache lookup failed for type %u", attrStruct->atttypid); /* * 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 = malloc(sizeof(PLpgSQL_var)); MemSet(var, 0, sizeof(PLpgSQL_var)); var->dtype = PLPGSQL_DTYPE_VAR; var->refname = malloc(strlen(relname) + strlen(attname) + 2); strcpy(var->refname, relname); strcat(var->refname, "."); strcat(var->refname, attname); var->datatype = build_datatype(typetup, attrStruct->atttypmod); var->isconst = false; var->notnull = false; var->default_val = NULL; var->value = (Datum) 0; var->isnull = true; var->freeval = false; plpgsql_adddatum((PLpgSQL_datum *) var); /* * Add the variable to the row. */ row->fieldnames[i] = strdup(attname); row->varnos[i] = var->varno; ReleaseSysCache(typetup); } 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;}/* ---------- * plpgsql_parse_datatype Scanner found something that should * be a datatype name. * ---------- */PLpgSQL_type *plpgsql_parse_datatype(char *string){ Oid type_id; int32 typmod; HeapTuple typeTup; PLpgSQL_type *typ; /* 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 */ typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(type_id), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", type_id); 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; typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type)); typ->typname = strdup(NameStr(typeStruct->typname)); typ->typoid = HeapTupleGetOid(typeTup); typ->typlen = typeStruct->typlen; typ->typbyval = typeStruct->typbyval; typ->typrelid = typeStruct->typrelid; typ->typelem = typeStruct->typelem; perm_fmgr_info(typeStruct->typinput, &(typ->typinput)); typ->atttypmod = typmod; return typ;}/* ---------- * plpgsql_adddatum Add a variable, record or row * to the compilers 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 *) malloc(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;}/* --------- * plpgsql_yyerror Handle parser error * --------- */voidplpgsql_yyerror(const char *s){ plpgsql_error_lineno = plpgsql_scanner_lineno(); ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), /* translator: first %s is a phrase like "syntax error" */ errmsg("%s at or near \"%s\"", s, plpgsql_yytext)));}/* * 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){ int i; /* 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 */ if (CALLED_AS_TRIGGER(fcinfo)) { TriggerData *trigdata = (TriggerData *) fcinfo->context; hashkey->trigrelOid = RelationGetRelid(trigdata->tg_relation); } /* get the argument types */ for (i = 0; i < procStruct->pronargs; i++) { Oid argtypeid = procStruct->proargtypes[i]; /* * Check for polymorphic arguments. If found, use the actual * parameter type from the caller's FuncExpr node, if we have one. * * We can support arguments of type ANY the same way as normal * polymorphic arguments. */ if (argtypeid == ANYARRAYOID || argtypeid == ANYELEMENTOID || argtypeid == ANYOID) { argtypeid = get_fn_expr_argtype(fcinfo->flinfo, i); if (!OidIsValid(argtypeid)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("could not determine actual argument " "type for polymorphic function \"%s\"", NameStr(procStruct->proname)))); } hashkey->argtypes[i] = argtypeid; }}/* exported so we can call it from plpgsql_init() */voidplpgsql_HashTableInit(void){ HASHCTL ctl; 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 (PLpgSQL_function *) 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 (hentry == NULL) ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); 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 + -