📄 pl_comp.c
字号:
function->fn_retset = false; /* shouldn't be any declared arguments */ if (procStruct->pronargs != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("trigger functions cannot have declared arguments"), errhint("You probably want to use TG_NARGS and TG_ARGV instead."))); /* Add the record for referencing NEW */ rec = palloc0(sizeof(PLpgSQL_rec)); rec->dtype = PLPGSQL_DTYPE_REC; rec->refname = pstrdup("new"); rec->tup = NULL; rec->tupdesc = NULL; rec->freetup = false; plpgsql_adddatum((PLpgSQL_datum *) rec); plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname); function->new_varno = rec->recno; /* Add the record for referencing OLD */ rec = palloc0(sizeof(PLpgSQL_rec)); rec->dtype = PLPGSQL_DTYPE_REC; rec->refname = pstrdup("old"); rec->tup = NULL; rec->tupdesc = NULL; rec->freetup = false; plpgsql_adddatum((PLpgSQL_datum *) rec); plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname); function->old_varno = rec->recno; /* Add the variable tg_name */ var = plpgsql_build_variable("tg_name", 0, plpgsql_build_datatype(NAMEOID, -1), true); function->tg_name_varno = var->dno; /* Add the variable tg_when */ var = plpgsql_build_variable("tg_when", 0, plpgsql_build_datatype(TEXTOID, -1), true); function->tg_when_varno = var->dno; /* Add the variable tg_level */ var = plpgsql_build_variable("tg_level", 0, plpgsql_build_datatype(TEXTOID, -1), true); function->tg_level_varno = var->dno; /* Add the variable tg_op */ var = plpgsql_build_variable("tg_op", 0, plpgsql_build_datatype(TEXTOID, -1), true); function->tg_op_varno = var->dno; /* Add the variable tg_relid */ var = plpgsql_build_variable("tg_relid", 0, plpgsql_build_datatype(OIDOID, -1), true); function->tg_relid_varno = var->dno; /* Add the variable tg_relname */ var = plpgsql_build_variable("tg_relname", 0, plpgsql_build_datatype(NAMEOID, -1), true); function->tg_relname_varno = var->dno; /* Add the variable tg_nargs */ var = plpgsql_build_variable("tg_nargs", 0, plpgsql_build_datatype(INT4OID, -1), true); function->tg_nargs_varno = var->dno; break; default: elog(ERROR, "unrecognized function typecode: %u", functype); break; } /* Remember if function is STABLE/IMMUTABLE */ function->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE); /* * Create the magic FOUND variable. */ var = plpgsql_build_variable("found", 0, plpgsql_build_datatype(BOOLOID, -1), true); function->found_varno = var->dno; /* * Forget about the above created variables */ plpgsql_add_initdatums(NULL); /* * Now parse the function's text */ parse_rc = plpgsql_yyparse(); if (parse_rc != 0) elog(ERROR, "plpgsql parser returned %d", parse_rc); function->action = plpgsql_yylval.program; plpgsql_scanner_finish(); pfree(proc_source); /* * If it has OUT parameters or returns VOID or returns a set, we allow * control to fall off the end without an explicit RETURN statement. The * easiest way to implement this is to add a RETURN statement to the end * of the statement list during parsing. However, if the outer block has * an EXCEPTION clause, we need to make a new outer block, since the added * RETURN shouldn't act like it is inside the EXCEPTION clause. */ if (num_out_args > 0 || function->fn_rettype == VOIDOID || function->fn_retset) { if (function->action->exceptions != NULL) { PLpgSQL_stmt_block *new; new = palloc0(sizeof(PLpgSQL_stmt_block)); new->cmd_type = PLPGSQL_STMT_BLOCK; new->body = list_make1(function->action); function->action = new; } if (function->action->body == NIL || ((PLpgSQL_stmt *) llast(function->action->body))->cmd_type != PLPGSQL_STMT_RETURN) { PLpgSQL_stmt_return *new; new = palloc0(sizeof(PLpgSQL_stmt_return)); new->cmd_type = PLPGSQL_STMT_RETURN; new->expr = NULL; new->retvarno = function->out_param_varno; function->action->body = lappend(function->action->body, new); } } /* * Complete the function's info */ function->fn_nargs = procStruct->pronargs; for (i = 0; i < function->fn_nargs; i++) function->fn_argvarnos[i] = in_arg_varnos[i]; function->ndatums = plpgsql_nDatums; function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums); for (i = 0; i < plpgsql_nDatums; i++) function->datums[i] = plpgsql_Datums[i]; /* Debug dump for completed functions */ if (plpgsql_DumpExecTree) plpgsql_dumptree(function); /* * add it to the hash table */ plpgsql_HashTableInsert(function, hashkey); /* * Pop the error context stack */ error_context_stack = plerrcontext.previous; plpgsql_error_funcname = NULL; plpgsql_error_lineno = 0; plpgsql_check_syntax = false; MemoryContextSwitchTo(compile_tmp_cxt); compile_tmp_cxt = NULL; return function;}/* * error context callback to let us supply a call-stack traceback. If * we are validating, the function source is passed as an * argument. This function is public only for the sake of an assertion * in gram.y */voidplpgsql_compile_error_callback(void *arg){ if (arg) { /* * Try to convert syntax error position to reference text of original * CREATE FUNCTION command. */ if (function_parse_error_transpose((const char *) arg)) return; /* * Done if a syntax error position was reported; otherwise we have to * fall back to a "near line N" report. */ } if (plpgsql_error_funcname) errcontext("compile of PL/pgSQL function \"%s\" near line %d", plpgsql_error_funcname, plpgsql_error_lineno);}/* * Fetch info about the argument types, names, and IN/OUT modes from the * pg_proc tuple. Return value is the number of arguments. * Other results are palloc'd. */static intfetchArgInfo(HeapTuple procTup, Oid **p_argtypes, char ***p_argnames, char **p_argmodes){ Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup); Datum proallargtypes; Datum proargmodes; Datum proargnames; bool isNull; ArrayType *arr; int numargs; Datum *elems; int nelems; int i; /* First discover the total number of parameters and get their types */ proallargtypes = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_proallargtypes, &isNull); if (!isNull) { /* * We expect the arrays to be 1-D arrays of the right types; verify * that. For the OID and char arrays, we don't need to use * deconstruct_array() since the array data is just going to look like * a C array of values. */ arr = DatumGetArrayTypeP(proallargtypes); /* ensure not toasted */ numargs = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 || numargs < 0 || ARR_ELEMTYPE(arr) != OIDOID) elog(ERROR, "proallargtypes is not a 1-D Oid array"); Assert(numargs >= procStruct->pronargs); *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid)); memcpy(*p_argtypes, ARR_DATA_PTR(arr), numargs * sizeof(Oid)); } else { /* If no proallargtypes, use proargtypes */ numargs = procStruct->proargtypes.dim1; Assert(numargs == procStruct->pronargs); *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid)); memcpy(*p_argtypes, procStruct->proargtypes.values, numargs * sizeof(Oid)); } /* Get argument names, if available */ proargnames = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_proargnames, &isNull); if (isNull) *p_argnames = NULL; else { deconstruct_array(DatumGetArrayTypeP(proargnames), TEXTOID, -1, false, 'i', &elems, &nelems); if (nelems != numargs) /* should not happen */ elog(ERROR, "proargnames must have the same number of elements as the function has arguments"); *p_argnames = (char **) palloc(sizeof(char *) * numargs); for (i = 0; i < numargs; i++) (*p_argnames)[i] = DatumGetCString(DirectFunctionCall1(textout, elems[i])); } /* Get argument modes, if available */ proargmodes = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_proargmodes, &isNull); if (isNull) *p_argmodes = NULL; else { arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ if (ARR_NDIM(arr) != 1 || ARR_DIMS(arr)[0] != numargs || ARR_ELEMTYPE(arr) != CHAROID) elog(ERROR, "proargmodes is not a 1-D char array"); *p_argmodes = (char *) palloc(numargs * sizeof(char)); memcpy(*p_argmodes, ARR_DATA_PTR(arr), numargs * sizeof(char)); } return numargs;}/* ---------- * plpgsql_parse_word The scanner calls this to postparse * any single word not found by a * keyword rule. * ---------- */intplpgsql_parse_word(char *word){ PLpgSQL_nsitem *nse; char *cp[1]; /* Do case conversion and word separation */ plpgsql_convert_ident(word, cp, 1); /* * Recognize tg_argv when compiling triggers */ if (plpgsql_curr_compile->fn_functype == T_TRIGGER) { if (strcmp(cp[0], "tg_argv") == 0) { bool save_spacescanned = plpgsql_SpaceScanned; PLpgSQL_trigarg *trigarg; trigarg = palloc0(sizeof(PLpgSQL_trigarg)); trigarg->dtype = PLPGSQL_DTYPE_TRIGARG; if (plpgsql_yylex() != '[') plpgsql_yyerror("expected \"[\""); trigarg->argnum = plpgsql_read_expression(']', "]"); plpgsql_adddatum((PLpgSQL_datum *) trigarg); plpgsql_yylval.scalar = (PLpgSQL_datum *) trigarg; plpgsql_SpaceScanned = save_spacescanned; pfree(cp[0]); return T_SCALAR; } } /* * Do a lookup on the compiler's namestack */ nse = plpgsql_ns_lookup(cp[0], NULL); if (nse != NULL) { pfree(cp[0]); switch (nse->itemtype) { case PLPGSQL_NSTYPE_LABEL: return T_LABEL; case PLPGSQL_NSTYPE_VAR: plpgsql_yylval.scalar = plpgsql_Datums[nse->itemno]; return T_SCALAR; case PLPGSQL_NSTYPE_REC: plpgsql_yylval.rec = (PLpgSQL_rec *) (plpgsql_Datums[nse->itemno]); return T_RECORD; case PLPGSQL_NSTYPE_ROW: plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[nse->itemno]); return T_ROW; default: return T_ERROR; } } /* * Nothing found - up to now it's a word without any special meaning for * us. */ pfree(cp[0]); return T_WORD;}/* ---------- * plpgsql_parse_dblword Same lookup for two words * separated by a dot. * ---------- */intplpgsql_parse_dblword(char *word){ PLpgSQL_nsitem *ns; char *cp[2]; /* Do case conversion and word separation */ plpgsql_convert_ident(word, cp, 2); /* * Lookup the first word */ ns = plpgsql_ns_lookup(cp[0], NULL); if (ns == NULL) { pfree(cp[0]); pfree(cp[1]); return T_ERROR; } switch (ns->itemtype) { case PLPGSQL_NSTYPE_LABEL: /* * First word is a label, so second word could be a variable, * record or row in that bodies namestack. Anything else could * only be something in a query given to the SPI manager and * T_ERROR will get eaten up by the collector routines. */ ns = plpgsql_ns_lookup(cp[1], cp[0]); pfree(cp[0]); pfree(cp[1]); if (ns == NULL) return T_ERROR; switch (ns->itemtype) { case PLPGSQL_NSTYPE_VAR: plpgsql_yylval.scalar = plpgsql_Datums[ns->itemno]; return T_SCALAR; case PLPGSQL_NSTYPE_REC: plpgsql_yylval.rec = (PLpgSQL_rec *) (plpgsql_Datums[ns->itemno]); return T_RECORD; case PLPGSQL_NSTYPE_ROW: plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]); return T_ROW; default: return T_ERROR; } break; case PLPGSQL_NSTYPE_REC: { /* * First word is a record name, so second word must be a field * in this record. */ PLpgSQL_recfield *new; new = palloc(sizeof(PLpgSQL_recfield)); new->dtype = PLPGSQL_DTYPE_RECFIELD; new->fieldname = pstrdup(cp[1]); new->recparentno = ns->itemno; plpgsql_adddatum((PLpgSQL_datum *) new); plpgsql_yylval.scalar = (PLpgSQL_datum *) new; pfree(cp[0]); pfree(cp[1]); return T_SCALAR; } case PLPGSQL_NSTYPE_ROW: { /* * First word is a row name, so second word must be a field in * this row. */ PLpgSQL_row *row; int i; row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]); for (i = 0; i < row->nfields; i++) { if (row->fieldnames[i] && strcmp(row->fieldnames[i], cp[1]) == 0) { plpgsql_yylval.scalar = plpgsql_Datums[row->varnos[i]]; pfree(cp[0]); pfree(cp[1]); return T_SCALAR; } } ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("row \"%s\" has no field \"%s\"", cp[0], cp[1]))); } default: break; } pfree(cp[0]); pfree(cp[1]); return T_ERROR;}/* ---------- * plpgsql_parse_tripword Same lookup for three words * separated by dots. * ---------- */intplpgsql_parse_tripword(char *word){ PLpgSQL_nsitem *ns; char *cp[3]; /* Do case conversion and word separation */ plpgsql_convert_ident(word, cp, 3); /* * Lookup the first word - it must be a label */ ns = plpgsql_ns_lookup(cp[0], NULL); if (ns == NULL) { pfree(cp[0]); pfree(cp[1]); pfree(cp[2]); return T_ERROR; } if (ns->itemtype != PLPGSQL_NSTYPE_LABEL) { pfree(cp[0]); pfree(cp[1]); pfree(cp[2]); return T_ERROR; } /* * First word is a label, so second word could be a record or row */ ns = plpgsql_ns_lookup(cp[1], cp[0]); if (ns == NULL) { pfree(cp[0]); pfree(cp[1]); pfree(cp[2]); return T_ERROR; } switch (ns->itemtype) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -