1369 lines
32 KiB
C
1369 lines
32 KiB
C
/**********************************************************************
|
|
* pl_comp.c - Compiler part of the PL/pgSQL
|
|
* procedural language
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.14 1999/12/20 01:41:32 tgl Exp $
|
|
*
|
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
|
*
|
|
* The author hereby grants permission to use, copy, modify,
|
|
* distribute, and license this software and its documentation
|
|
* for any purpose, provided that existing copyright notices are
|
|
* retained in all copies and that this notice is included
|
|
* verbatim in any distributions. No written agreement, license,
|
|
* or royalty fee is required for any of the authorized uses.
|
|
* Modifications to this software may be copyrighted by their
|
|
* author and need not follow the licensing terms described
|
|
* here, provided that the new terms are clearly indicated on
|
|
* the first page of each file where they apply.
|
|
*
|
|
* IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY
|
|
* PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
|
|
* CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
|
|
* SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN
|
|
* IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
* DAMAGE.
|
|
*
|
|
* THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
* PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON
|
|
* AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO
|
|
* OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
|
|
* ENHANCEMENTS, OR MODIFICATIONS.
|
|
*
|
|
**********************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "plpgsql.h"
|
|
#include "pl.tab.h"
|
|
|
|
#include "executor/spi.h"
|
|
#include "commands/trigger.h"
|
|
#include "utils/builtins.h"
|
|
#include "fmgr.h"
|
|
#include "access/heapam.h"
|
|
|
|
#include "utils/syscache.h"
|
|
#include "utils/catcache.h"
|
|
#include "catalog/catname.h"
|
|
#include "catalog/pg_proc.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "catalog/pg_class.h"
|
|
#include "catalog/pg_attribute.h"
|
|
#include "catalog/pg_attrdef.h"
|
|
|
|
|
|
/* ----------
|
|
* Variables in the parser that shouldn't go into plpgsql.h
|
|
* ----------
|
|
*/
|
|
extern PLPGSQL_YYSTYPE plpgsql_yylval;
|
|
extern char plpgsql_yytext[];
|
|
extern int plpgsql_yylineno;
|
|
|
|
/* ----------
|
|
* Our own local and global variables
|
|
* ----------
|
|
*/
|
|
static int datums_alloc;
|
|
int plpgsql_nDatums;
|
|
PLpgSQL_datum **plpgsql_Datums;
|
|
static int datums_last = 0;
|
|
|
|
int plpgsql_error_lineno;
|
|
char *plpgsql_error_funcname;
|
|
int plpgsql_DumpExecTree = 0;
|
|
|
|
PLpgSQL_function *plpgsql_curr_compile;
|
|
|
|
|
|
/* ----------
|
|
* Local function declarations
|
|
* ----------
|
|
*/
|
|
extern void plpgsql_yyerror(const char *s);
|
|
|
|
static char *xlateSqlType(char *name);
|
|
|
|
|
|
/* ----------
|
|
* plpgsql_compile Given a pg_proc's oid, make
|
|
* an execution tree for it.
|
|
* ----------
|
|
*/
|
|
PLpgSQL_function *
|
|
plpgsql_compile(Oid fn_oid, int functype)
|
|
{
|
|
int parse_rc;
|
|
HeapTuple procTup;
|
|
Form_pg_proc procStruct;
|
|
HeapTuple typeTup;
|
|
Form_pg_type typeStruct;
|
|
char *proc_source;
|
|
PLpgSQL_function *function;
|
|
PLpgSQL_var *var;
|
|
PLpgSQL_row *row;
|
|
PLpgSQL_rec *rec;
|
|
int i;
|
|
int arg_varnos[MAXFMGRARGS];
|
|
|
|
/* ----------
|
|
* Initialize the compiler
|
|
* ----------
|
|
*/
|
|
plpgsql_ns_init();
|
|
plpgsql_ns_push(NULL);
|
|
plpgsql_DumpExecTree = 0;
|
|
|
|
datums_alloc = 128;
|
|
plpgsql_nDatums = 0;
|
|
plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc);
|
|
datums_last = 0;
|
|
|
|
/* ----------
|
|
* Lookup the pg_proc tuple by Oid
|
|
* ----------
|
|
*/
|
|
procTup = SearchSysCacheTuple(PROCOID,
|
|
ObjectIdGetDatum(fn_oid),
|
|
0, 0, 0);
|
|
if (!HeapTupleIsValid(procTup))
|
|
elog(ERROR, "plpgsql: cache lookup from pg_proc failed");
|
|
|
|
/* ----------
|
|
* Setup the scanner input and error info
|
|
* ----------
|
|
*/
|
|
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
|
|
proc_source = textout(&(procStruct->prosrc));
|
|
plpgsql_setinput(proc_source, functype);
|
|
plpgsql_error_funcname = nameout(&(procStruct->proname));
|
|
plpgsql_error_lineno = 0;
|
|
|
|
/* ----------
|
|
* Create the new function node
|
|
* ----------
|
|
*/
|
|
function = malloc(sizeof(PLpgSQL_function));
|
|
memset(function, 0, sizeof(PLpgSQL_function));
|
|
plpgsql_curr_compile = function;
|
|
|
|
function->fn_functype = functype;
|
|
function->fn_oid = fn_oid;
|
|
function->fn_name = strdup(nameout(&(procStruct->proname)));
|
|
|
|
switch (functype)
|
|
{
|
|
case T_FUNCTION:
|
|
/* ----------
|
|
* Normal function has a defined returntype
|
|
* ----------
|
|
*/
|
|
function->fn_rettype = procStruct->prorettype;
|
|
function->fn_retset = procStruct->proretset;
|
|
|
|
/* ----------
|
|
* Lookup the functions return type
|
|
* ----------
|
|
*/
|
|
typeTup = SearchSysCacheTuple(TYPEOID,
|
|
ObjectIdGetDatum(procStruct->prorettype), 0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(typeTup))
|
|
{
|
|
plpgsql_comperrinfo();
|
|
elog(ERROR, "cache lookup for return type %u failed",
|
|
procStruct->prorettype);
|
|
}
|
|
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
|
|
if (typeStruct->typrelid != InvalidOid)
|
|
function->fn_retistuple = true;
|
|
else
|
|
{
|
|
function->fn_retbyval = typeStruct->typbyval;
|
|
function->fn_rettyplen = typeStruct->typlen;
|
|
fmgr_info(typeStruct->typinput, &(function->fn_retinput));
|
|
}
|
|
|
|
/* ----------
|
|
* Create the variables for the procedures parameters
|
|
* ----------
|
|
*/
|
|
for (i = 0; i < procStruct->pronargs; i++)
|
|
{
|
|
char buf[256];
|
|
|
|
/* ----------
|
|
* Get the parameters type
|
|
* ----------
|
|
*/
|
|
typeTup = SearchSysCacheTuple(TYPEOID,
|
|
ObjectIdGetDatum(procStruct->proargtypes[i]), 0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(typeTup))
|
|
{
|
|
plpgsql_comperrinfo();
|
|
elog(ERROR, "cache lookup for argument type %u failed",
|
|
procStruct->proargtypes[i]);
|
|
}
|
|
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
|
|
|
|
if (typeStruct->typrelid != InvalidOid)
|
|
{
|
|
/* ----------
|
|
* For tuple type parameters, we set up a record
|
|
* of that type
|
|
* ----------
|
|
*/
|
|
sprintf(buf, "%s%%rowtype", nameout(&(typeStruct->typname)));
|
|
if (plpgsql_parse_wordrowtype(buf) != T_ROW)
|
|
{
|
|
plpgsql_comperrinfo();
|
|
elog(ERROR, "cannot get tuple struct of argument %d", i + 1);
|
|
}
|
|
|
|
row = plpgsql_yylval.row;
|
|
sprintf(buf, "$%d", i + 1);
|
|
|
|
row->refname = strdup(buf);
|
|
|
|
plpgsql_adddatum((PLpgSQL_datum *) row);
|
|
plpgsql_ns_additem(PLPGSQL_NSTYPE_ROW, row->rowno, buf);
|
|
|
|
arg_varnos[i] = row->rowno;
|
|
}
|
|
else
|
|
{
|
|
/* ----------
|
|
* Normal parameters get a var node
|
|
* ----------
|
|
*/
|
|
var = malloc(sizeof(PLpgSQL_var));
|
|
memset(var, 0, sizeof(PLpgSQL_var));
|
|
var->datatype = malloc(sizeof(PLpgSQL_type));
|
|
memset(var->datatype, 0, sizeof(PLpgSQL_type));
|
|
|
|
sprintf(buf, "$%d", i + 1);
|
|
var->dtype = PLPGSQL_DTYPE_VAR;
|
|
var->refname = strdup(buf);
|
|
var->lineno = 0;
|
|
var->datatype->typname = strdup(nameout(&(typeStruct->typname)));
|
|
var->datatype->typoid = procStruct->proargtypes[i];
|
|
fmgr_info(typeStruct->typinput, &(var->datatype->typinput));
|
|
var->datatype->typbyval = typeStruct->typbyval;
|
|
var->datatype->atttypmod = -1;
|
|
var->isconst = true;
|
|
var->notnull = false;
|
|
var->default_val = NULL;
|
|
|
|
plpgsql_adddatum((PLpgSQL_datum *) var);
|
|
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, buf);
|
|
|
|
arg_varnos[i] = var->varno;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case T_TRIGGER:
|
|
/* ----------
|
|
* Trigger procedures return type is unknown yet
|
|
* ----------
|
|
*/
|
|
function->fn_rettype = InvalidOid;
|
|
function->fn_retbyval = false;
|
|
function->fn_retistuple = true;
|
|
function->fn_retset = false;
|
|
|
|
/* ----------
|
|
* Add the record for referencing NEW
|
|
* ----------
|
|
*/
|
|
rec = malloc(sizeof(PLpgSQL_rec));
|
|
memset(rec, 0, sizeof(PLpgSQL_rec));
|
|
rec->dtype = PLPGSQL_DTYPE_REC;
|
|
rec->refname = strdup("new");
|
|
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 = malloc(sizeof(PLpgSQL_rec));
|
|
memset(rec, 0, sizeof(PLpgSQL_rec));
|
|
rec->dtype = PLPGSQL_DTYPE_REC;
|
|
rec->refname = strdup("old");
|
|
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 = malloc(sizeof(PLpgSQL_var));
|
|
memset(var, 0, sizeof(PLpgSQL_var));
|
|
|
|
var->dtype = PLPGSQL_DTYPE_VAR;
|
|
var->refname = strdup("tg_name");
|
|
var->lineno = 0;
|
|
plpgsql_parse_word("name");
|
|
var->datatype = plpgsql_yylval.dtype;
|
|
var->isconst = false;
|
|
var->notnull = false;
|
|
var->default_val = NULL;
|
|
|
|
plpgsql_adddatum((PLpgSQL_datum *) var);
|
|
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);
|
|
function->tg_name_varno = var->varno;
|
|
|
|
/* ----------
|
|
* Add the variable tg_when
|
|
* ----------
|
|
*/
|
|
var = malloc(sizeof(PLpgSQL_var));
|
|
memset(var, 0, sizeof(PLpgSQL_var));
|
|
|
|
var->dtype = PLPGSQL_DTYPE_VAR;
|
|
var->refname = strdup("tg_when");
|
|
var->lineno = 0;
|
|
plpgsql_parse_word("text");
|
|
var->datatype = plpgsql_yylval.dtype;
|
|
var->isconst = false;
|
|
var->notnull = false;
|
|
var->default_val = NULL;
|
|
|
|
plpgsql_adddatum((PLpgSQL_datum *) var);
|
|
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);
|
|
function->tg_when_varno = var->varno;
|
|
|
|
/* ----------
|
|
* Add the variable tg_level
|
|
* ----------
|
|
*/
|
|
var = malloc(sizeof(PLpgSQL_var));
|
|
memset(var, 0, sizeof(PLpgSQL_var));
|
|
|
|
var->dtype = PLPGSQL_DTYPE_VAR;
|
|
var->refname = strdup("tg_level");
|
|
var->lineno = 0;
|
|
plpgsql_parse_word("text");
|
|
var->datatype = plpgsql_yylval.dtype;
|
|
var->isconst = false;
|
|
var->notnull = false;
|
|
var->default_val = NULL;
|
|
|
|
plpgsql_adddatum((PLpgSQL_datum *) var);
|
|
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);
|
|
function->tg_level_varno = var->varno;
|
|
|
|
/* ----------
|
|
* Add the variable tg_op
|
|
* ----------
|
|
*/
|
|
var = malloc(sizeof(PLpgSQL_var));
|
|
memset(var, 0, sizeof(PLpgSQL_var));
|
|
|
|
var->dtype = PLPGSQL_DTYPE_VAR;
|
|
var->refname = strdup("tg_op");
|
|
var->lineno = 0;
|
|
plpgsql_parse_word("text");
|
|
var->datatype = plpgsql_yylval.dtype;
|
|
var->isconst = false;
|
|
var->notnull = false;
|
|
var->default_val = NULL;
|
|
|
|
plpgsql_adddatum((PLpgSQL_datum *) var);
|
|
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);
|
|
function->tg_op_varno = var->varno;
|
|
|
|
/* ----------
|
|
* Add the variable tg_relid
|
|
* ----------
|
|
*/
|
|
var = malloc(sizeof(PLpgSQL_var));
|
|
memset(var, 0, sizeof(PLpgSQL_var));
|
|
|
|
var->dtype = PLPGSQL_DTYPE_VAR;
|
|
var->refname = strdup("tg_relid");
|
|
var->lineno = 0;
|
|
plpgsql_parse_word("oid");
|
|
var->datatype = plpgsql_yylval.dtype;
|
|
var->isconst = false;
|
|
var->notnull = false;
|
|
var->default_val = NULL;
|
|
|
|
plpgsql_adddatum((PLpgSQL_datum *) var);
|
|
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);
|
|
function->tg_relid_varno = var->varno;
|
|
|
|
/* ----------
|
|
* Add the variable tg_relname
|
|
* ----------
|
|
*/
|
|
var = malloc(sizeof(PLpgSQL_var));
|
|
memset(var, 0, sizeof(PLpgSQL_var));
|
|
|
|
var->dtype = PLPGSQL_DTYPE_VAR;
|
|
var->refname = strdup("tg_relname");
|
|
var->lineno = 0;
|
|
plpgsql_parse_word("name");
|
|
var->datatype = plpgsql_yylval.dtype;
|
|
var->isconst = false;
|
|
var->notnull = false;
|
|
var->default_val = NULL;
|
|
|
|
plpgsql_adddatum((PLpgSQL_datum *) var);
|
|
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);
|
|
function->tg_relname_varno = var->varno;
|
|
|
|
/* ----------
|
|
* Add the variable tg_nargs
|
|
* ----------
|
|
*/
|
|
var = malloc(sizeof(PLpgSQL_var));
|
|
memset(var, 0, sizeof(PLpgSQL_var));
|
|
|
|
var->dtype = PLPGSQL_DTYPE_VAR;
|
|
var->refname = strdup("tg_nargs");
|
|
var->lineno = 0;
|
|
plpgsql_parse_word("int4");
|
|
var->datatype = plpgsql_yylval.dtype;
|
|
var->isconst = false;
|
|
var->notnull = false;
|
|
var->default_val = NULL;
|
|
|
|
plpgsql_adddatum((PLpgSQL_datum *) var);
|
|
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname);
|
|
function->tg_nargs_varno = var->varno;
|
|
|
|
break;
|
|
|
|
default:
|
|
elog(ERROR, "unknown function type %u in plpgsql_compile()",
|
|
functype);
|
|
break;
|
|
}
|
|
|
|
/* ----------
|
|
* Create the magic found variable indicating if the
|
|
* last FOR or SELECT statement returned data
|
|
* ----------
|
|
*/
|
|
var = malloc(sizeof(PLpgSQL_var));
|
|
memset(var, 0, sizeof(PLpgSQL_var));
|
|
|
|
var->dtype = PLPGSQL_DTYPE_VAR;
|
|
var->refname = strdup("found");
|
|
var->lineno = 0;
|
|
plpgsql_parse_word("bool");
|
|
var->datatype = plpgsql_yylval.dtype;
|
|
var->isconst = false;
|
|
var->notnull = false;
|
|
var->default_val = NULL;
|
|
|
|
plpgsql_adddatum((PLpgSQL_datum *) var);
|
|
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, strdup("found"));
|
|
function->found_varno = var->varno;
|
|
|
|
/* ----------
|
|
* Forget about the above created variables
|
|
* ----------
|
|
*/
|
|
plpgsql_add_initdatums(NULL);
|
|
|
|
/* ----------
|
|
* Now parse the functions text
|
|
* ----------
|
|
*/
|
|
parse_rc = plpgsql_yyparse();
|
|
if (parse_rc != 0)
|
|
{
|
|
plpgsql_comperrinfo();
|
|
elog(ERROR, "plpgsql: parser returned %d ???", parse_rc);
|
|
}
|
|
|
|
/* ----------
|
|
* If that was successful, complete the functions info.
|
|
* ----------
|
|
*/
|
|
function->fn_nargs = procStruct->pronargs;
|
|
for (i = 0; i < function->fn_nargs; i++)
|
|
function->fn_argvarnos[i] = arg_varnos[i];
|
|
function->ndatums = plpgsql_nDatums;
|
|
function->datums = malloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
|
|
for (i = 0; i < plpgsql_nDatums; i++)
|
|
function->datums[i] = plpgsql_Datums[i];
|
|
function->action = plpgsql_yylval.program;
|
|
|
|
|
|
/* ----------
|
|
* Finally return the compiled function
|
|
* ----------
|
|
*/
|
|
if (plpgsql_DumpExecTree)
|
|
plpgsql_dumptree(function);
|
|
return function;
|
|
}
|
|
|
|
|
|
/* ----------
|
|
* plpgsql_parse_word The scanner calls this to postparse
|
|
* any single word not found by a
|
|
* keyword rule.
|
|
* ----------
|
|
*/
|
|
int
|
|
plpgsql_parse_word(char *word)
|
|
{
|
|
PLpgSQL_nsitem *nse;
|
|
char *cp;
|
|
HeapTuple typeTup;
|
|
Form_pg_type typeStruct;
|
|
char *typeXlated;
|
|
|
|
/* ----------
|
|
* We do our lookups case insensitive
|
|
* ----------
|
|
*/
|
|
cp = plpgsql_tolower(word);
|
|
|
|
/* ----------
|
|
* Special handling when compiling triggers
|
|
* ----------
|
|
*/
|
|
if (plpgsql_curr_compile->fn_functype == T_TRIGGER)
|
|
{
|
|
if (!strcmp(cp, "tg_argv"))
|
|
{
|
|
int save_spacescanned = plpgsql_SpaceScanned;
|
|
PLpgSQL_trigarg *trigarg;
|
|
|
|
trigarg = malloc(sizeof(PLpgSQL_trigarg));
|
|
memset(trigarg, 0, 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.trigarg = trigarg;
|
|
|
|
plpgsql_SpaceScanned = save_spacescanned;
|
|
return T_TGARGV;
|
|
}
|
|
}
|
|
|
|
/* ----------
|
|
* Do a lookup on the compilers namestack
|
|
* ----------
|
|
*/
|
|
nse = plpgsql_ns_lookup(cp, NULL);
|
|
if (nse != NULL)
|
|
{
|
|
pfree(cp);
|
|
switch (nse->itemtype)
|
|
{
|
|
case PLPGSQL_NSTYPE_LABEL:
|
|
return T_LABEL;
|
|
|
|
case PLPGSQL_NSTYPE_VAR:
|
|
plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[nse->itemno]);
|
|
return T_VARIABLE;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* ----------
|
|
* Try to find a data type with that name, but ignore
|
|
* pg_type entries that are in fact class types.
|
|
* ----------
|
|
*/
|
|
typeXlated = xlateSqlType(cp);
|
|
typeTup = SearchSysCacheTuple(TYPENAME,
|
|
PointerGetDatum(typeXlated), 0, 0, 0);
|
|
if (HeapTupleIsValid(typeTup))
|
|
{
|
|
PLpgSQL_type *typ;
|
|
|
|
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
|
|
|
|
if (typeStruct->typrelid != InvalidOid)
|
|
{
|
|
pfree(cp);
|
|
return T_WORD;
|
|
}
|
|
|
|
typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type));
|
|
|
|
typ->typname = strdup(nameout(&(typeStruct->typname)));
|
|
typ->typoid = typeTup->t_data->t_oid;
|
|
fmgr_info(typeStruct->typinput, &(typ->typinput));
|
|
typ->typbyval = typeStruct->typbyval;
|
|
typ->atttypmod = -1;
|
|
|
|
plpgsql_yylval.dtype = typ;
|
|
|
|
pfree(cp);
|
|
return T_DTYPE;
|
|
}
|
|
|
|
/* ----------
|
|
* Nothing found - up to now it's a word without any
|
|
* special meaning for us.
|
|
* ----------
|
|
*/
|
|
pfree(cp);
|
|
return T_WORD;
|
|
}
|
|
|
|
|
|
/* ----------
|
|
* plpgsql_parse_dblword Same lookup for two words
|
|
* separated by a dot.
|
|
* ----------
|
|
*/
|
|
int
|
|
plpgsql_parse_dblword(char *string)
|
|
{
|
|
char *word1;
|
|
char *word2;
|
|
PLpgSQL_nsitem *ns;
|
|
|
|
/* ----------
|
|
* Convert to lower case and separate the words
|
|
* ----------
|
|
*/
|
|
word1 = plpgsql_tolower(string);
|
|
word2 = strchr(word1, '.');
|
|
*word2++ = '\0';
|
|
|
|
/* ----------
|
|
* Lookup the first word
|
|
* ----------
|
|
*/
|
|
ns = plpgsql_ns_lookup(word1, NULL);
|
|
if (ns == NULL)
|
|
{
|
|
pfree(word1);
|
|
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(word2, word1);
|
|
if (ns == NULL)
|
|
{
|
|
pfree(word1);
|
|
return T_ERROR;
|
|
}
|
|
switch (ns->itemtype)
|
|
{
|
|
case PLPGSQL_NSTYPE_VAR:
|
|
plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[ns->itemno]);
|
|
pfree(word1);
|
|
return T_VARIABLE;
|
|
|
|
case PLPGSQL_NSTYPE_REC:
|
|
plpgsql_yylval.rec = (PLpgSQL_rec *) (plpgsql_Datums[ns->itemno]);
|
|
pfree(word1);
|
|
return T_RECORD;
|
|
|
|
case PLPGSQL_NSTYPE_ROW:
|
|
plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
|
|
pfree(word1);
|
|
return T_ROW;
|
|
|
|
default:
|
|
pfree(word1);
|
|
return T_ERROR;
|
|
}
|
|
|
|
case PLPGSQL_NSTYPE_REC:
|
|
{
|
|
/* ----------
|
|
* First word is a record name, so second word
|
|
* must be a field in this record.
|
|
* ----------
|
|
*/
|
|
PLpgSQL_recfield *new;
|
|
|
|
new = malloc(sizeof(PLpgSQL_recfield));
|
|
new->dtype = PLPGSQL_DTYPE_RECFIELD;
|
|
new->fieldname = strdup(word2);
|
|
new->recno = ns->itemno;
|
|
|
|
plpgsql_adddatum((PLpgSQL_datum *) new);
|
|
|
|
pfree(word1);
|
|
plpgsql_yylval.recfield = new;
|
|
return T_RECFIELD;
|
|
}
|
|
|
|
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 (!strcmp(row->fieldnames[i], word2))
|
|
{
|
|
plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[row->varnos[i]]);
|
|
pfree(word1);
|
|
return T_VARIABLE;
|
|
}
|
|
}
|
|
plpgsql_comperrinfo();
|
|
elog(ERROR, "row %s doesn't have a field %s",
|
|
word1, word2);
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
pfree(word1);
|
|
return T_ERROR;
|
|
}
|
|
|
|
|
|
/* ----------
|
|
* plpgsql_parse_tripword Same lookup for three words
|
|
* separated by dots.
|
|
* ----------
|
|
*/
|
|
int
|
|
plpgsql_parse_tripword(char *string)
|
|
{
|
|
char *word1;
|
|
char *word2;
|
|
char *word3;
|
|
PLpgSQL_nsitem *ns;
|
|
|
|
/* ----------
|
|
* Convert to lower case and separate the words
|
|
* ----------
|
|
*/
|
|
word1 = plpgsql_tolower(string);
|
|
word2 = strchr(word1, '.');
|
|
*word2++ = '\0';
|
|
word3 = strchr(word2, '.');
|
|
*word3++ = '\0';
|
|
|
|
/* ----------
|
|
* Lookup the first word - it must be a label
|
|
* ----------
|
|
*/
|
|
ns = plpgsql_ns_lookup(word1, NULL);
|
|
if (ns == NULL)
|
|
{
|
|
pfree(word1);
|
|
return T_ERROR;
|
|
}
|
|
if (ns->itemtype != PLPGSQL_NSTYPE_LABEL)
|
|
{
|
|
pfree(word1);
|
|
return T_ERROR;
|
|
}
|
|
|
|
/* ----------
|
|
* First word is a label, so second word could be
|
|
* a record or row
|
|
* ----------
|
|
*/
|
|
ns = plpgsql_ns_lookup(word2, word1);
|
|
if (ns == NULL)
|
|
{
|
|
pfree(word1);
|
|
return T_ERROR;
|
|
}
|
|
|
|
switch (ns->itemtype)
|
|
{
|
|
case PLPGSQL_NSTYPE_REC:
|
|
{
|
|
/* ----------
|
|
* This word is a record name, so third word
|
|
* must be a field in this record.
|
|
* ----------
|
|
*/
|
|
PLpgSQL_recfield *new;
|
|
|
|
new = malloc(sizeof(PLpgSQL_recfield));
|
|
new->dtype = PLPGSQL_DTYPE_RECFIELD;
|
|
new->fieldname = strdup(word3);
|
|
new->recno = ns->itemno;
|
|
|
|
plpgsql_adddatum((PLpgSQL_datum *) new);
|
|
|
|
pfree(word1);
|
|
plpgsql_yylval.recfield = new;
|
|
return T_RECFIELD;
|
|
}
|
|
|
|
case PLPGSQL_NSTYPE_ROW:
|
|
{
|
|
/* ----------
|
|
* This word is 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 (!strcmp(row->fieldnames[i], word3))
|
|
{
|
|
plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[row->varnos[i]]);
|
|
pfree(word1);
|
|
return T_VARIABLE;
|
|
}
|
|
}
|
|
plpgsql_comperrinfo();
|
|
elog(ERROR, "row %s.%s doesn't have a field %s",
|
|
word1, word2, word3);
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
pfree(word1);
|
|
return T_ERROR;
|
|
}
|
|
|
|
|
|
/* ----------
|
|
* plpgsql_parse_wordtype The scanner found word%TYPE. word can be
|
|
* a variable name or a basetype.
|
|
* ----------
|
|
*/
|
|
int
|
|
plpgsql_parse_wordtype(char *word)
|
|
{
|
|
PLpgSQL_nsitem *nse;
|
|
char *cp;
|
|
HeapTuple typeTup;
|
|
Form_pg_type typeStruct;
|
|
char *typeXlated;
|
|
bool old_nsstate;
|
|
|
|
/* ----------
|
|
* We do our lookups case insensitive
|
|
* ----------
|
|
*/
|
|
cp = plpgsql_tolower(word);
|
|
*(strchr(cp, '%')) = '\0';
|
|
|
|
/* ----------
|
|
* Do a lookup on the compilers namestack.
|
|
* But ensure it moves up to the toplevel.
|
|
* ----------
|
|
*/
|
|
old_nsstate = plpgsql_ns_setlocal(false);
|
|
nse = plpgsql_ns_lookup(cp, NULL);
|
|
plpgsql_ns_setlocal(old_nsstate);
|
|
|
|
if (nse != NULL)
|
|
{
|
|
pfree(cp);
|
|
switch (nse->itemtype)
|
|
{
|
|
case PLPGSQL_NSTYPE_VAR:
|
|
plpgsql_yylval.dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
|
|
return T_DTYPE;
|
|
|
|
default:
|
|
return T_ERROR;
|
|
}
|
|
}
|
|
|
|
/* ----------
|
|
* Word wasn't found on the namestack.
|
|
* Try to find a data type with that name, but ignore
|
|
* pg_type entries that are in fact class types.
|
|
* ----------
|
|
*/
|
|
typeXlated = xlateSqlType(cp);
|
|
typeTup = SearchSysCacheTuple(TYPENAME,
|
|
PointerGetDatum(typeXlated), 0, 0, 0);
|
|
if (HeapTupleIsValid(typeTup))
|
|
{
|
|
PLpgSQL_type *typ;
|
|
|
|
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
|
|
|
|
if (typeStruct->typrelid != InvalidOid)
|
|
{
|
|
pfree(cp);
|
|
return T_ERROR;
|
|
}
|
|
|
|
typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type));
|
|
|
|
typ->typname = strdup(nameout(&(typeStruct->typname)));
|
|
typ->typoid = typeTup->t_data->t_oid;
|
|
fmgr_info(typeStruct->typinput, &(typ->typinput));
|
|
typ->typbyval = typeStruct->typbyval;
|
|
typ->atttypmod = -1;
|
|
|
|
plpgsql_yylval.dtype = typ;
|
|
|
|
pfree(cp);
|
|
return T_DTYPE;
|
|
}
|
|
|
|
/* ----------
|
|
* Nothing found - up to now it's a word without any
|
|
* special meaning for us.
|
|
* ----------
|
|
*/
|
|
pfree(cp);
|
|
return T_ERROR;
|
|
}
|
|
|
|
|
|
/* ----------
|
|
* plpgsql_parse_dblwordtype Same lookup for word.word%TYPE
|
|
* ----------
|
|
*/
|
|
int
|
|
plpgsql_parse_dblwordtype(char *string)
|
|
{
|
|
char *word1;
|
|
char *word2;
|
|
PLpgSQL_nsitem *nse;
|
|
bool old_nsstate;
|
|
HeapTuple classtup;
|
|
Form_pg_class classStruct;
|
|
HeapTuple attrtup;
|
|
Form_pg_attribute attrStruct;
|
|
HeapTuple typetup;
|
|
Form_pg_type typeStruct;
|
|
PLpgSQL_type *typ;
|
|
|
|
|
|
/* ----------
|
|
* Convert to lower case and separate the words
|
|
* ----------
|
|
*/
|
|
word1 = plpgsql_tolower(string);
|
|
word2 = strchr(word1, '.');
|
|
*word2++ = '\0';
|
|
*(strchr(word2, '%')) = '\0';
|
|
|
|
/* ----------
|
|
* Lookup the first word
|
|
* ----------
|
|
*/
|
|
nse = plpgsql_ns_lookup(word1, NULL);
|
|
|
|
/* ----------
|
|
* If this is a label lookup the second word in that
|
|
* labels namestack level
|
|
* ----------
|
|
*/
|
|
if (nse != NULL)
|
|
{
|
|
if (nse->itemtype == PLPGSQL_NSTYPE_LABEL)
|
|
{
|
|
old_nsstate = plpgsql_ns_setlocal(false);
|
|
nse = plpgsql_ns_lookup(word2, word1);
|
|
plpgsql_ns_setlocal(old_nsstate);
|
|
|
|
pfree(word1);
|
|
|
|
if (nse != NULL)
|
|
{
|
|
switch (nse->itemtype)
|
|
{
|
|
case PLPGSQL_NSTYPE_VAR:
|
|
plpgsql_yylval.dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
|
|
return T_DTYPE;
|
|
|
|
default:
|
|
return T_ERROR;
|
|
}
|
|
}
|
|
return T_ERROR;
|
|
}
|
|
pfree(word1);
|
|
return T_ERROR;
|
|
}
|
|
|
|
/* ----------
|
|
* First word could also be a table name
|
|
* ----------
|
|
*/
|
|
classtup = SearchSysCacheTuple(RELNAME,
|
|
PointerGetDatum(word1), 0, 0, 0);
|
|
if (!HeapTupleIsValid(classtup))
|
|
{
|
|
pfree(word1);
|
|
return T_ERROR;
|
|
}
|
|
|
|
/* ----------
|
|
* It must be a (shared) relation class
|
|
* ----------
|
|
*/
|
|
classStruct = (Form_pg_class) GETSTRUCT(classtup);
|
|
if (classStruct->relkind != 'r' && classStruct->relkind != 's')
|
|
{
|
|
pfree(word1);
|
|
return T_ERROR;
|
|
}
|
|
|
|
/* ----------
|
|
* Fetch the named table field and it's type
|
|
* ----------
|
|
*/
|
|
attrtup = SearchSysCacheTuple(ATTNAME,
|
|
ObjectIdGetDatum(classtup->t_data->t_oid),
|
|
PointerGetDatum(word2), 0, 0);
|
|
if (!HeapTupleIsValid(attrtup))
|
|
{
|
|
pfree(word1);
|
|
return T_ERROR;
|
|
}
|
|
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
|
|
|
|
typetup = SearchSysCacheTuple(TYPEOID,
|
|
ObjectIdGetDatum(attrStruct->atttypid), 0, 0, 0);
|
|
if (!HeapTupleIsValid(typetup))
|
|
{
|
|
plpgsql_comperrinfo();
|
|
elog(ERROR, "cache lookup for type %u of %s.%s failed",
|
|
attrStruct->atttypid, word1, word2);
|
|
}
|
|
|
|
/* ----------
|
|
* Found that - build a compiler type struct and return it
|
|
* ----------
|
|
*/
|
|
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
|
|
|
|
typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type));
|
|
|
|
typ->typname = strdup(nameout(&(typeStruct->typname)));
|
|
typ->typoid = typetup->t_data->t_oid;
|
|
fmgr_info(typeStruct->typinput, &(typ->typinput));
|
|
typ->typbyval = typeStruct->typbyval;
|
|
typ->atttypmod = attrStruct->atttypmod;
|
|
|
|
plpgsql_yylval.dtype = typ;
|
|
|
|
pfree(word1);
|
|
return T_DTYPE;
|
|
}
|
|
|
|
|
|
/* ----------
|
|
* plpgsql_parse_wordrowtype Scanner found word%ROWTYPE.
|
|
* So word must be a table name.
|
|
* ----------
|
|
*/
|
|
int
|
|
plpgsql_parse_wordrowtype(char *string)
|
|
{
|
|
HeapTuple classtup;
|
|
Form_pg_class classStruct;
|
|
HeapTuple typetup;
|
|
Form_pg_type typeStruct;
|
|
HeapTuple attrtup;
|
|
Form_pg_attribute attrStruct;
|
|
char *word1;
|
|
char *cp;
|
|
int i;
|
|
PLpgSQL_row *row;
|
|
PLpgSQL_var *var;
|
|
|
|
/* ----------
|
|
* Get the word in lower case and fetch the pg_class tuple.
|
|
* ----------
|
|
*/
|
|
word1 = plpgsql_tolower(string);
|
|
cp = strchr(word1, '%');
|
|
*cp = '\0';
|
|
|
|
classtup = SearchSysCacheTuple(RELNAME,
|
|
PointerGetDatum(word1), 0, 0, 0);
|
|
if (!HeapTupleIsValid(classtup))
|
|
{
|
|
plpgsql_comperrinfo();
|
|
elog(ERROR, "%s: no such class", word1);
|
|
}
|
|
classStruct = (Form_pg_class) GETSTRUCT(classtup);
|
|
if (classStruct->relkind != 'r' && classStruct->relkind != 's')
|
|
{
|
|
plpgsql_comperrinfo();
|
|
elog(ERROR, "%s isn't a table", word1);
|
|
}
|
|
|
|
/* ----------
|
|
* Fetch the tables pg_type tuple too
|
|
* ----------
|
|
*/
|
|
typetup = SearchSysCacheTuple(TYPENAME,
|
|
PointerGetDatum(word1), 0, 0, 0);
|
|
if (!HeapTupleIsValid(typetup))
|
|
{
|
|
plpgsql_comperrinfo();
|
|
elog(ERROR, "cache lookup for %s in pg_type failed", word1);
|
|
}
|
|
|
|
/* ----------
|
|
* 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;
|
|
row->nfields = classStruct->relnatts;
|
|
row->rowtypeclass = typetup->t_data->t_oid;
|
|
row->fieldnames = malloc(sizeof(char *) * row->nfields);
|
|
row->varnos = malloc(sizeof(int) * row->nfields);
|
|
|
|
for (i = 0; i < row->nfields; i++)
|
|
{
|
|
/* ----------
|
|
* Get the attribute and it's type
|
|
* ----------
|
|
*/
|
|
attrtup = SearchSysCacheTuple(ATTNUM,
|
|
ObjectIdGetDatum(classtup->t_data->t_oid),
|
|
(Datum) (i + 1), 0, 0);
|
|
if (!HeapTupleIsValid(attrtup))
|
|
{
|
|
plpgsql_comperrinfo();
|
|
elog(ERROR, "cache lookup for attribute %d of class %s failed",
|
|
i + 1, word1);
|
|
}
|
|
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
|
|
|
|
typetup = SearchSysCacheTuple(TYPEOID,
|
|
ObjectIdGetDatum(attrStruct->atttypid), 0, 0, 0);
|
|
if (!HeapTupleIsValid(typetup))
|
|
{
|
|
plpgsql_comperrinfo();
|
|
elog(ERROR, "cache lookup for type %u of %s.%s failed",
|
|
attrStruct->atttypid, word1,
|
|
nameout(&(attrStruct->attname)));
|
|
}
|
|
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
|
|
|
|
cp = strdup(nameout(&(attrStruct->attname)));
|
|
|
|
/* ----------
|
|
* Create the internal variable
|
|
* 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));
|
|
var->dtype = PLPGSQL_DTYPE_VAR;
|
|
var->refname = malloc(strlen(word1) + strlen(cp) + 2);
|
|
strcpy(var->refname, word1);
|
|
strcat(var->refname, ".");
|
|
strcat(var->refname, cp);
|
|
var->datatype = malloc(sizeof(PLpgSQL_type));
|
|
var->datatype->typname = strdup(NameStr(typeStruct->typname));
|
|
var->datatype->typoid = typetup->t_data->t_oid;
|
|
fmgr_info(typeStruct->typinput, &(var->datatype->typinput));
|
|
var->datatype->typbyval = typeStruct->typbyval;
|
|
var->datatype->atttypmod = attrStruct->atttypmod;
|
|
var->isconst = false;
|
|
var->notnull = false;
|
|
var->default_val = NULL;
|
|
var->value = (Datum) 0;
|
|
var->isnull = true;
|
|
var->shouldfree = false;
|
|
|
|
plpgsql_adddatum((PLpgSQL_datum *) var);
|
|
|
|
/* ----------
|
|
* Add the variable to the row.
|
|
* ----------
|
|
*/
|
|
row->fieldnames[i] = cp;
|
|
row->varnos[i] = var->varno;
|
|
}
|
|
|
|
/* ----------
|
|
* Return the complete row definition
|
|
* ----------
|
|
*/
|
|
plpgsql_yylval.row = row;
|
|
|
|
return T_ROW;
|
|
}
|
|
|
|
|
|
/* ----------
|
|
* plpgsql_adddatum Add a variable, record or row
|
|
* to the compilers datum list.
|
|
* ----------
|
|
*/
|
|
void
|
|
plpgsql_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.
|
|
* ----------
|
|
*/
|
|
int
|
|
plpgsql_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)
|
|
{
|
|
*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;
|
|
}
|
|
}
|
|
}
|
|
|
|
datums_last = plpgsql_nDatums;
|
|
return n;
|
|
}
|
|
|
|
|
|
/* ----------
|
|
* plpgsql_comperrinfo Called before elog(ERROR, ...)
|
|
* during compile.
|
|
* ----------
|
|
*/
|
|
void
|
|
plpgsql_comperrinfo()
|
|
{
|
|
elog(NOTICE, "plpgsql: ERROR during compile of %s near line %d",
|
|
plpgsql_error_funcname, plpgsql_error_lineno);
|
|
}
|
|
|
|
|
|
/* ---------
|
|
* plpgsql_yyerror Handle parser error
|
|
* ---------
|
|
*/
|
|
|
|
void
|
|
plpgsql_yyerror(const char *s)
|
|
{
|
|
plpgsql_error_lineno = plpgsql_yylineno;
|
|
plpgsql_comperrinfo();
|
|
elog(ERROR, "%s at or near \"%s\"", s, plpgsql_yytext);
|
|
}
|
|
|
|
|
|
/* ----------
|
|
* xlateSqlType()
|
|
* Convert alternate type names to internal Postgres types.
|
|
*
|
|
* Stolen from backend's main parser
|
|
* ----------
|
|
*/
|
|
static char *
|
|
xlateSqlType(char *name)
|
|
{
|
|
if (!strcasecmp(name, "int")
|
|
|| !strcasecmp(name, "integer"))
|
|
return "int4";
|
|
else if (!strcasecmp(name, "smallint"))
|
|
return "int2";
|
|
else if (!strcasecmp(name, "real")
|
|
|| !strcasecmp(name, "float"))
|
|
return "float8";
|
|
else if (!strcasecmp(name, "interval"))
|
|
return "timespan";
|
|
else if (!strcasecmp(name, "boolean"))
|
|
return "bool";
|
|
else
|
|
return name;
|
|
} /* xlateSqlType() */
|