pl_comp.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,081 行 · 第 1/4 页
C
2,081 行
if (rettypeid == VOIDOID || rettypeid == RECORDOID) /* okay */ ; else if (rettypeid == TRIGGEROID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("trigger functions can only be called as triggers"))); else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plpgsql functions cannot return type %s", format_type_be(rettypeid)))); } if (typeStruct->typrelid != InvalidOid || rettypeid == RECORDOID) function->fn_retistuple = true; else { function->fn_retbyval = typeStruct->typbyval; function->fn_rettyplen = typeStruct->typlen; function->fn_rettypioparam = getTypeIOParam(typeTup); fmgr_info(typeStruct->typinput, &(function->fn_retinput)); /* * install $0 reference, but only for polymorphic return * types, and not when the return is specified through an * output parameter. */ if (IsPolymorphicType(procStruct->prorettype) && num_out_args == 0) { (void) plpgsql_build_variable("$0", 0, build_datatype(typeTup, -1), true); } } ReleaseSysCache(typeTup); break; case T_TRIGGER: /* Trigger procedure's return type is unknown yet */ function->fn_rettype = InvalidOid; function->fn_retbyval = false; function->fn_retistuple = true; 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; /* tg_table_name is now preferred to tg_relname */ var = plpgsql_build_variable("tg_table_name", 0, plpgsql_build_datatype(NAMEOID, -1), true); function->tg_table_name_varno = var->dno; /* add variable tg_table_schema */ var = plpgsql_build_variable("tg_table_schema", 0, plpgsql_build_datatype(NAMEOID, -1), true); function->tg_table_schema_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; /* * 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);}/* ---------- * plpgsql_parse_word The scanner calls this to postparse * any single word not found by a * keyword rule. * ---------- */intplpgsql_parse_word(const 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 * (XXX this sucks, it should be a regular variable in the namestack) */ 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, NULL, NULL); pfree(cp[0]); if (nse != NULL) { switch (nse->itemtype) { 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. */ return T_WORD;}/* ---------- * plpgsql_parse_dblword Same lookup for two words * separated by a dot. * ---------- */intplpgsql_parse_dblword(const char *word){ PLpgSQL_nsitem *ns; char *cp[2]; int nnames; /* Do case conversion and word separation */ plpgsql_convert_ident(word, cp, 2); /* * Do a lookup on the compiler's namestack */ ns = plpgsql_ns_lookup(cp[0], cp[1], NULL, &nnames); if (ns == NULL) { pfree(cp[0]); pfree(cp[1]); return T_ERROR; } switch (ns->itemtype) { case PLPGSQL_NSTYPE_VAR: /* Block-qualified reference to scalar variable. */ plpgsql_yylval.scalar = plpgsql_Datums[ns->itemno]; pfree(cp[0]); pfree(cp[1]); return T_SCALAR; case PLPGSQL_NSTYPE_REC: if (nnames == 1) { /* * 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; } else { /* Block-qualified reference to record variable. */ plpgsql_yylval.rec = (PLpgSQL_rec *) (plpgsql_Datums[ns->itemno]); pfree(cp[0]); pfree(cp[1]); return T_RECORD; } case PLPGSQL_NSTYPE_ROW: if (nnames == 1) { /* * 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]))); } else { /* Block-qualified reference to row variable. */ plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]); pfree(cp[0]); pfree(cp[1]); return T_ROW; } 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(const char *word){ PLpgSQL_nsitem *ns; char *cp[3]; int nnames; /* Do case conversion and word separation */ plpgsql_convert_ident(word, cp, 3); /* * Do a lookup on the compiler's namestack. * Must find a qualified reference. */ ns = plpgsql_ns_lookup(cp[0], cp[1], cp[2], &nnames); if (ns == NULL || nnames != 2) { pfree(cp[0]); pfree(cp[1]); pfree(cp[2]); return T_ERROR; } switch (ns->itemtype) { case PLPGSQL_NSTYPE_REC: { /* * words 1/2 are a record name, so third 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[2]); new->recparentno = ns->itemno; plpgsql_adddatum((PLpgSQL_datum *) new); plpgsql_yylval.scalar = (PLpgSQL_datum *) new; pfree(cp[0]); pfree(cp[1]); pfree(cp[2]); return T_SCALAR; } case PLPGSQL_NSTYPE_ROW: { /* * words 1/2 are a row name, so third 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[2]) == 0) { plpgsql_yylval.scalar = plpgsql_Datums[row->varnos[i]];
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?