diff --git a/doc/src/sgml/plsql.sgml b/doc/src/sgml/plsql.sgml index a2d2395f91..d7f1b99d1b 100644 --- a/doc/src/sgml/plsql.sgml +++ b/doc/src/sgml/plsql.sgml @@ -1,5 +1,5 @@ @@ -229,7 +229,7 @@ END; re-create them. For example: drop function testfunc(integer); -create function testfunc(integer) return integer as ' +create function testfunc(integer) returns integer as ' .... end; ' language 'plpgsql'; @@ -360,7 +360,7 @@ END; Here are some examples of variable declarations: user_id INTEGER; -quantity NUMBER(5); +quantity NUMERIC(5); url VARCHAR; @@ -437,7 +437,7 @@ END; Using the %TYPE and %ROWTYPE attributes, you can declare variables with the same - data type or structure of another database item (e.g: a + data type or structure as another database item (e.g: a table field). @@ -512,7 +512,7 @@ create function cs_refresh_one_mv(integer) returns integer as ' WHERE sort_key=key; IF NOT FOUND THEN - RAISE EXCEPTION ''View '' || key || '' not found''; + RAISE EXCEPTION ''View % not found'', key; RETURN 0; END IF; @@ -575,8 +575,7 @@ SELECT expression identifiers are substituted by parameters and the actual values from the variables are passed to the executor in the parameter array. All expressions used in a PL/pgSQL function are only prepared and - saved once. The only exception to this rule is an EXECUTE statement - if parsing of a query is needed each time it is encountered. + saved once. The only exception to this rule is an EXECUTE statement. diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 856affbdbe..ed010a46ed 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -8,13 +8,16 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.35 2001/03/22 03:59:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.36 2001/10/09 04:15:38 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "catalog/pg_type.h" +#include "nodes/parsenodes.h" +#include "parser/parser.h" +#include "parser/parse_expr.h" #include "parser/parse_type.h" #include "utils/syscache.h" @@ -263,3 +266,73 @@ typenameTypeId(char *s) ReleaseSysCache(typ); return result; } + +/* + * Given a string that is supposed to be a SQL-compatible type declaration, + * such as "int4" or "integer" or "character varying(32)", parse + * the string and convert it to a type OID and type modifier. + * + * This routine is not currently used by the main backend, but it is + * exported for use by add-on modules such as plpgsql, in hopes of + * centralizing parsing knowledge about SQL type declarations. + */ +void +parseTypeString(const char *str, Oid *type_id, int32 *typmod) +{ + char *buf; + List *raw_parsetree_list; + SelectStmt *stmt; + ResTarget *restarget; + A_Const *aconst; + TypeName *typename; + + buf = (char *) palloc(strlen(str) + 16); + sprintf(buf, "SELECT (NULL::%s)", str); + + raw_parsetree_list = parser(buf, NULL, 0); + + /* + * Make sure we got back exactly what we expected and no more; + * paranoia is justified since the string might contain anything. + */ + if (length(raw_parsetree_list) != 1) + elog(ERROR, "Invalid type name '%s'", str); + stmt = (SelectStmt *) lfirst(raw_parsetree_list); + if (stmt == NULL || + !IsA(stmt, SelectStmt) || + stmt->distinctClause != NIL || + stmt->into != NULL || + stmt->fromClause != NIL || + stmt->whereClause != NULL || + stmt->groupClause != NIL || + stmt->havingClause != NULL || + stmt->sortClause != NIL || + stmt->portalname != NULL || + stmt->limitOffset != NULL || + stmt->limitCount != NULL || + stmt->forUpdate != NIL || + stmt->op != SETOP_NONE) + elog(ERROR, "Invalid type name '%s'", str); + if (length(stmt->targetList) != 1) + elog(ERROR, "Invalid type name '%s'", str); + restarget = (ResTarget *) lfirst(stmt->targetList); + if (restarget == NULL || + !IsA(restarget, ResTarget) || + restarget->name != NULL || + restarget->indirection != NIL) + elog(ERROR, "Invalid type name '%s'", str); + aconst = (A_Const *) restarget->val; + if (aconst == NULL || + !IsA(aconst, A_Const) || + aconst->val.type != T_Null) + elog(ERROR, "Invalid type name '%s'", str); + typename = aconst->typename; + if (typename == NULL || + !IsA(typename, TypeName)) + elog(ERROR, "Invalid type name '%s'", str); + + *type_id = typenameTypeId(TypeNameToInternalName(typename)); + *typmod = typename->typmod; + + pfree(buf); +} diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h index dee44cfe10..baa31c972b 100644 --- a/src/include/parser/parse_type.h +++ b/src/include/parser/parse_type.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_type.h,v 1.16 2001/01/24 19:43:27 momjian Exp $ + * $Id: parse_type.h,v 1.17 2001/10/09 04:15:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -34,6 +34,8 @@ extern char *typeidTypeName(Oid id); extern Oid typeidTypeRelid(Oid type_id); extern Oid typenameTypeId(char *s); +extern void parseTypeString(const char *str, Oid *type_id, int32 *typmod); + #define ISCOMPLEX(typeid) (typeidTypeRelid(typeid) != InvalidOid) #endif /* PARSE_TYPE_H */ diff --git a/src/pl/plpgsql/src/Makefile b/src/pl/plpgsql/src/Makefile index bc62d95a01..914b57d115 100644 --- a/src/pl/plpgsql/src/Makefile +++ b/src/pl/plpgsql/src/Makefile @@ -2,7 +2,7 @@ # # Makefile for the plpgsql shared object # -# $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Makefile,v 1.18 2001/09/16 16:11:11 petere Exp $ +# $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Makefile,v 1.19 2001/10/09 04:15:38 tgl Exp $ # #------------------------------------------------------------------------- @@ -23,7 +23,7 @@ override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) override DLLLIBS := $(BE_DLLLIBS) $(DLLLIBS) rpath := -OBJS = pl_parse.o pl_handler.o pl_comp.o pl_exec.o pl_funcs.o +OBJS = pl_gram.o pl_scan.o pl_handler.o pl_comp.o pl_exec.o pl_funcs.o ifneq ($(PORTNAME), qnx4) all: all-lib @@ -59,10 +59,7 @@ installdirs: uninstall: rm -f $(DESTDIR)$(pkglibdir)/plpgsql$(DLSUFFIX) -pl_handler.o pl_comp.o pl_exec.o pl_funcs.o: plpgsql.h $(srcdir)/pl.tab.h - -pl_parse.o: $(srcdir)/pl_gram.c $(srcdir)/pl_scan.c plpgsql.h - $(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $< +pl_gram.o pl_scan.o pl_handler.o pl_comp.o pl_exec.o pl_funcs.o: plpgsql.h $(srcdir)/pl.tab.h # Note: Since the yacc and lex files are shipped in the distribution, # they must be generated in the srcdir (as opposed to builddir). @@ -79,17 +76,18 @@ endif $(srcdir)/pl_scan.c: scan.l ifdef FLEX - $(FLEX) -i -l $(FLEXFLAGS) $< - sed -e 's/yy/plpgsql_yy/g' -e 's/YY/PLPGSQL_YY/g' < lex.yy.c > $@ - rm -f lex.yy.c + $(FLEX) -i $(FLEXFLAGS) -Pplpgsql_base_yy -o'$@' $< else @$(missing) flex $< $@ endif distprep: $(srcdir)/pl_scan.c $(srcdir)/pl.tab.h $(srcdir)/pl_gram.c +# pl_gram.c, pl.tab.h, and pl_scan.c are in the distribution tarball, +# so they are not cleaned here. clean distclean: clean-lib rm -f $(OBJS) +# And the garbage that might have been left behind by partial build: @rm -f y.tab.c y.tab.h lex.yy.c maintainer-clean: clean diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index ad45bd2d49..a2d37c3f99 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -4,7 +4,7 @@ * procedural language * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.25 2001/09/26 21:35:28 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.26 2001/10/09 04:15:38 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -36,16 +36,11 @@ * **********************************************************************/ -#include -#include #include "plpgsql.h" -#ifdef YYBISON -#include "pl_scan.c" /* GNU bison wants it here */ -#endif - static PLpgSQL_expr *read_sqlstmt(int until, char *s, char *sqlstart); +static PLpgSQL_type *read_datatype(int tok); static PLpgSQL_stmt *make_select_stmt(void); static PLpgSQL_stmt *make_fetch_stmt(void); static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row); @@ -99,9 +94,9 @@ static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row); %type decl_sect %type decl_varname %type decl_renname -%type decl_const, decl_notnull, decl_atttypmod, decl_atttypmodval +%type decl_const, decl_notnull %type decl_defval, decl_cursor_query -%type decl_datatype, decl_dtypename +%type decl_datatype %type decl_rowtype, decl_cursor_args, decl_cursor_arglist %type decl_aliasitem %type decl_stmts, decl_stmt @@ -189,9 +184,6 @@ static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row); */ %token T_FUNCTION %token T_TRIGGER -%token T_CHAR -%token T_BPCHAR -%token T_VARCHAR %token T_LABEL %token T_STRING %token T_VARIABLE @@ -394,8 +386,7 @@ decl_statement : decl_varname decl_const decl_datatype decl_notnull decl_defval curname_def->query = strdup(buf); new->default_val = curname_def; - plpgsql_parse_word("refcursor"); - new->datatype = yylval.dtype; + new->datatype = plpgsql_parse_datatype("refcursor"); new->cursor_explicit_expr = $6; if ($3 == NULL) @@ -554,48 +545,14 @@ decl_const : { $$ = 1; } ; -decl_datatype : decl_dtypename - { $$ = $1; } - ; - -decl_dtypename : T_DTYPE - { $$ = yylval.dtype; } - | T_CHAR decl_atttypmod +decl_datatype : { - if ($2 < 0) - { - plpgsql_parse_word("char"); - $$ = yylval.dtype; - } else - { - plpgsql_parse_word("bpchar"); - $$ = yylval.dtype; - $$->atttypmod = $2; - } - } - | T_VARCHAR decl_atttypmod - { - plpgsql_parse_word("varchar"); - $$ = yylval.dtype; - $$->atttypmod = $2; - } - | T_BPCHAR '(' decl_atttypmodval ')' - { - plpgsql_parse_word("bpchar"); - $$ = yylval.dtype; - $$->atttypmod = $3; - } - ; - -decl_atttypmod : - { $$ = -1; } - | '(' decl_atttypmodval ')' - { $$ = $2; } - ; - -decl_atttypmodval : T_NUMBER - { - $$ = pg_atoi(yytext, sizeof(int16), '\0') + VARHDRSZ; + /* + * If there's a lookahead token, read_datatype + * should consume it. + */ + $$ = read_datatype(yychar); + yyclearin; } ; @@ -1007,9 +964,7 @@ fori_var : fori_varname new->refname = $1.name; new->lineno = $1.lineno; - plpgsql_parse_word("integer"); - - new->datatype = yylval.dtype; + new->datatype = plpgsql_parse_datatype("integer"); new->isconst = false; new->notnull = false; new->default_val = NULL; @@ -1558,10 +1513,6 @@ lno : %% -#ifndef YYBISON -#include "pl_scan.c" /* BSD yacc wants it here */ -#endif - PLpgSQL_expr * plpgsql_read_expression (int until, char *s) @@ -1592,6 +1543,12 @@ read_sqlstmt (int until, char *s, char *sqlstart) plpgsql_dstring_append(&ds, " "); switch (tok) { + case 0: + plpgsql_error_lineno = lno; + plpgsql_comperrinfo(); + elog(ERROR, "missing %s at end of SQL statement", s); + break; + case T_VARIABLE: params[nparams] = yylval.var->varno; sprintf(buf, " $%d ", ++nparams); @@ -1611,12 +1568,6 @@ read_sqlstmt (int until, char *s, char *sqlstart) break; default: - if (tok == 0) - { - plpgsql_error_lineno = lno; - plpgsql_comperrinfo(); - elog(ERROR, "missing %s at end of SQL statement", s); - } plpgsql_dstring_append(&ds, yytext); break; } @@ -1634,6 +1585,64 @@ read_sqlstmt (int until, char *s, char *sqlstart) return expr; } +static PLpgSQL_type * +read_datatype(int tok) +{ + int lno; + PLpgSQL_dstring ds; + PLpgSQL_type *result; + bool needspace = false; + int parenlevel = 0; + + lno = yylineno; + + /* Often there will be a lookahead token, but if not, get one */ + if (tok == YYEMPTY) + tok = yylex(); + + if (tok == T_DTYPE) + { + /* lexer found word%TYPE and did its thing already */ + return yylval.dtype; + } + + plpgsql_dstring_init(&ds); + + while (tok != ';') + { + if (tok == 0) + { + plpgsql_error_lineno = lno; + plpgsql_comperrinfo(); + elog(ERROR, "incomplete datatype declaration"); + } + /* Possible followers for datatype in a declaration */ + if (tok == K_NOT || tok == K_ASSIGN || tok == K_DEFAULT) + break; + /* Possible followers for datatype in a cursor_arg list */ + if ((tok == ',' || tok == ')') && parenlevel == 0) + break; + if (tok == '(') + parenlevel++; + else if (tok == ')') + parenlevel--; + if (needspace) + plpgsql_dstring_append(&ds, " "); + needspace = true; + plpgsql_dstring_append(&ds, yytext); + + tok = yylex(); + } + + plpgsql_push_back_token(tok); + + result = plpgsql_parse_datatype(plpgsql_dstring_get(&ds)); + + plpgsql_dstring_free(&ds); + + return result; +} + static PLpgSQL_stmt * make_select_stmt() diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 5c7e12ad19..c06a2c8781 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.34 2001/10/06 23:21:44 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.35 2001/10/09 04:15:38 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -35,15 +35,12 @@ * **********************************************************************/ -#include -#include -#include +#include "plpgsql.h" + #include #include -#include #include -#include "plpgsql.h" #include "pl.tab.h" #include "access/heapam.h" @@ -57,6 +54,7 @@ #include "executor/spi.h" #include "fmgr.h" #include "parser/gramparse.h" +#include "parser/parse_type.h" #include "utils/builtins.h" #include "utils/syscache.h" @@ -66,8 +64,6 @@ * ---------- */ extern PLPGSQL_YYSTYPE plpgsql_yylval; -extern char plpgsql_yytext[]; -extern int plpgsql_yylineno; /* ---------- * Our own local and global variables @@ -152,8 +148,7 @@ plpgsql_compile(Oid fn_oid, int functype) proc_source = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(&procStruct->prosrc))); plpgsql_setinput(proc_source, functype); - plpgsql_error_funcname = DatumGetCString(DirectFunctionCall1(nameout, - NameGetDatum(&(procStruct->proname)))); + plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname)); plpgsql_error_lineno = 0; /* @@ -165,8 +160,7 @@ plpgsql_compile(Oid fn_oid, int functype) function->fn_functype = functype; function->fn_oid = fn_oid; - function->fn_name = strdup(DatumGetCString(DirectFunctionCall1(nameout, - NameGetDatum(&(procStruct->proname))))); + function->fn_name = strdup(NameStr(procStruct->proname)); switch (functype) { @@ -237,9 +231,7 @@ plpgsql_compile(Oid fn_oid, int functype) * For tuple type parameters, we set up a record of * that type */ - sprintf(buf, "%s%%rowtype", - DatumGetCString(DirectFunctionCall1(nameout, - NameGetDatum(&(typeStruct->typname))))); + sprintf(buf, "%s%%rowtype", NameStr(typeStruct->typname)); if (plpgsql_parse_wordrowtype(buf) != T_ROW) { plpgsql_comperrinfo(); @@ -272,8 +264,7 @@ plpgsql_compile(Oid fn_oid, int functype) var->dtype = PLPGSQL_DTYPE_VAR; var->refname = strdup(buf); var->lineno = 0; - var->datatype->typname = DatumGetCString(DirectFunctionCall1(nameout, - NameGetDatum(&(typeStruct->typname)))); + var->datatype->typname = strdup(NameStr(typeStruct->typname)); var->datatype->typoid = procStruct->proargtypes[i]; perm_fmgr_info(typeStruct->typinput, &(var->datatype->typinput)); var->datatype->typelem = typeStruct->typelem; @@ -340,8 +331,7 @@ plpgsql_compile(Oid fn_oid, int functype) var->dtype = PLPGSQL_DTYPE_VAR; var->refname = strdup("tg_name"); var->lineno = 0; - plpgsql_parse_word("name"); - var->datatype = plpgsql_yylval.dtype; + var->datatype = plpgsql_parse_datatype("name"); var->isconst = false; var->notnull = false; var->default_val = NULL; @@ -359,8 +349,7 @@ plpgsql_compile(Oid fn_oid, int functype) var->dtype = PLPGSQL_DTYPE_VAR; var->refname = strdup("tg_when"); var->lineno = 0; - plpgsql_parse_word("text"); - var->datatype = plpgsql_yylval.dtype; + var->datatype = plpgsql_parse_datatype("text"); var->isconst = false; var->notnull = false; var->default_val = NULL; @@ -378,8 +367,7 @@ plpgsql_compile(Oid fn_oid, int functype) var->dtype = PLPGSQL_DTYPE_VAR; var->refname = strdup("tg_level"); var->lineno = 0; - plpgsql_parse_word("text"); - var->datatype = plpgsql_yylval.dtype; + var->datatype = plpgsql_parse_datatype("text"); var->isconst = false; var->notnull = false; var->default_val = NULL; @@ -397,8 +385,7 @@ plpgsql_compile(Oid fn_oid, int functype) var->dtype = PLPGSQL_DTYPE_VAR; var->refname = strdup("tg_op"); var->lineno = 0; - plpgsql_parse_word("text"); - var->datatype = plpgsql_yylval.dtype; + var->datatype = plpgsql_parse_datatype("text"); var->isconst = false; var->notnull = false; var->default_val = NULL; @@ -416,8 +403,7 @@ plpgsql_compile(Oid fn_oid, int functype) var->dtype = PLPGSQL_DTYPE_VAR; var->refname = strdup("tg_relid"); var->lineno = 0; - plpgsql_parse_word("oid"); - var->datatype = plpgsql_yylval.dtype; + var->datatype = plpgsql_parse_datatype("oid"); var->isconst = false; var->notnull = false; var->default_val = NULL; @@ -435,8 +421,7 @@ plpgsql_compile(Oid fn_oid, int functype) var->dtype = PLPGSQL_DTYPE_VAR; var->refname = strdup("tg_relname"); var->lineno = 0; - plpgsql_parse_word("name"); - var->datatype = plpgsql_yylval.dtype; + var->datatype = plpgsql_parse_datatype("name"); var->isconst = false; var->notnull = false; var->default_val = NULL; @@ -454,8 +439,7 @@ plpgsql_compile(Oid fn_oid, int functype) var->dtype = PLPGSQL_DTYPE_VAR; var->refname = strdup("tg_nargs"); var->lineno = 0; - plpgsql_parse_word("int4"); - var->datatype = plpgsql_yylval.dtype; + var->datatype = plpgsql_parse_datatype("int4"); var->isconst = false; var->notnull = false; var->default_val = NULL; @@ -482,8 +466,7 @@ plpgsql_compile(Oid fn_oid, int functype) var->dtype = PLPGSQL_DTYPE_VAR; var->refname = strdup("found"); var->lineno = 0; - plpgsql_parse_word("bool"); - var->datatype = plpgsql_yylval.dtype; + var->datatype = plpgsql_parse_datatype("bool"); var->isconst = false; var->notnull = false; var->default_val = NULL; @@ -541,9 +524,6 @@ plpgsql_parse_word(char *word) { PLpgSQL_nsitem *nse; char *cp; - HeapTuple typeTup; - Form_pg_type typeStruct; - char *typeXlated; /* * We do our lookups case insensitive @@ -606,45 +586,6 @@ plpgsql_parse_word(char *word) } } - /* - * Try to find a data type with that name, but ignore pg_type entries - * that are in fact class types. - */ - typeXlated = xlateSqlType(cp); - typeTup = SearchSysCache(TYPENAME, - PointerGetDatum(typeXlated), - 0, 0, 0); - if (HeapTupleIsValid(typeTup)) - { - PLpgSQL_type *typ; - - typeStruct = (Form_pg_type) GETSTRUCT(typeTup); - - if (typeStruct->typrelid != InvalidOid) - { - ReleaseSysCache(typeTup); - pfree(cp); - return T_WORD; - } - - typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type)); - - typ->typname = DatumGetCString(DirectFunctionCall1(nameout, - NameGetDatum(&(typeStruct->typname)))); - typ->typoid = typeTup->t_data->t_oid; - perm_fmgr_info(typeStruct->typinput, &(typ->typinput)); - typ->typelem = typeStruct->typelem; - typ->typbyval = typeStruct->typbyval; - typ->typlen = typeStruct->typlen; - typ->atttypmod = -1; - - plpgsql_yylval.dtype = typ; - - ReleaseSysCache(typeTup); - pfree(cp); - return T_DTYPE; - } - /* * Nothing found - up to now it's a word without any special meaning * for us. @@ -946,8 +887,7 @@ plpgsql_parse_wordtype(char *word) typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type)); - typ->typname = DatumGetCString(DirectFunctionCall1(nameout, - NameGetDatum(&(typeStruct->typname)))); + typ->typname = strdup(NameStr(typeStruct->typname)); typ->typoid = typeTup->t_data->t_oid; perm_fmgr_info(typeStruct->typinput, &(typ->typinput)); typ->typelem = typeStruct->typelem; @@ -1090,8 +1030,7 @@ plpgsql_parse_dblwordtype(char *string) */ typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type)); - typ->typname = DatumGetCString(DirectFunctionCall1(nameout, - NameGetDatum(&(typeStruct->typname)))); + typ->typname = strdup(NameStr(typeStruct->typname)); typ->typoid = typetup->t_data->t_oid; perm_fmgr_info(typeStruct->typinput, &(typ->typinput)); typ->typelem = typeStruct->typelem; @@ -1199,8 +1138,7 @@ plpgsql_parse_wordrowtype(char *string) } attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup); - cp = DatumGetCString(DirectFunctionCall1(nameout, - NameGetDatum(&(attrStruct->attname)))); + cp = pstrdup(NameStr(attrStruct->attname)); typetup = SearchSysCache(TYPEOID, ObjectIdGetDatum(attrStruct->atttypid), @@ -1268,6 +1206,47 @@ plpgsql_parse_wordrowtype(char *string) } +/* ---------- + * 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; + Form_pg_type typeStruct; + 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); + typeStruct = (Form_pg_type) GETSTRUCT(typeTup); + + typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type)); + + typ->typname = strdup(NameStr(typeStruct->typname)); + typ->typoid = type_id; + perm_fmgr_info(typeStruct->typinput, &(typ->typinput)); + typ->typelem = typeStruct->typelem; + typ->typbyval = typeStruct->typbyval; + typ->typlen = typeStruct->typlen; + typ->atttypmod = typmod; + + ReleaseSysCache(typeTup); + + return typ; +} + + /* ---------- * plpgsql_adddatum Add a variable, record or row * to the compilers datum list. diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index c5942fa069..fc2032dbd6 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.17 2001/08/02 21:31:23 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.18 2001/10/09 04:15:38 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -523,8 +523,13 @@ extern PLpgSQL_datum **plpgsql_Datums; extern int plpgsql_error_lineno; extern char *plpgsql_error_funcname; -extern PLpgSQL_function *plpgsql_curr_compile; +/* linkage to the real yytext and yylineno variables */ +extern char *plpgsql_base_yytext; +#define plpgsql_yytext plpgsql_base_yytext +extern int plpgsql_base_yylineno; +#define plpgsql_yylineno plpgsql_base_yylineno +extern PLpgSQL_function *plpgsql_curr_compile; /********************************************************************** * Function declarations @@ -541,9 +546,11 @@ extern int plpgsql_parse_tripword(char *string); extern int plpgsql_parse_wordtype(char *string); extern int plpgsql_parse_dblwordtype(char *string); extern int plpgsql_parse_wordrowtype(char *string); +extern PLpgSQL_type *plpgsql_parse_datatype(char *string); extern void plpgsql_adddatum(PLpgSQL_datum * new); extern int plpgsql_add_initdatums(int **varnos); extern void plpgsql_comperrinfo(void); +extern void plpgsql_yyerror(const char *s); /* ---------- * Functions in pl_handler.c @@ -594,10 +601,10 @@ extern char *plpgsql_tolower(char *s); * ---------- */ extern PLpgSQL_expr *plpgsql_read_expression(int until, char *s); -extern void plpgsql_yyrestart(FILE *fp); -extern int plpgsql_yylex(void); -extern void plpgsql_setinput(char *s, int functype); extern int plpgsql_yyparse(void); -extern void plpgsql_yyerror(const char *s); +extern int plpgsql_base_yylex(void); +extern int plpgsql_yylex(void); +extern void plpgsql_push_back_token(int token); +extern void plpgsql_setinput(char *s, int functype); #endif /* PLPGSQL_H */ diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l index 6e17365705..dfb16f8177 100644 --- a/src/pl/plpgsql/src/scan.l +++ b/src/pl/plpgsql/src/scan.l @@ -4,7 +4,7 @@ * procedural language * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.14 2001/07/12 17:42:08 momjian Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.15 2001/10/09 04:15:38 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -36,21 +36,28 @@ * **********************************************************************/ +#include "plpgsql.h" +#include "pl.tab.h" + + static char *plpgsql_source; static int plpgsql_bytes_left; static int scanner_functype; static int scanner_typereported; +static int pushback_token; +static bool have_pushback_token; int plpgsql_SpaceScanned = 0; -extern int yylineno; - static void plpgsql_input(char *buf, int *result, int max); #define YY_INPUT(buf,res,max) plpgsql_input(buf, &res, max) #define YY_NO_UNPUT %} +%option yylineno + + WS [\200-\377_A-Za-z"] WC [\200-\377_A-Za-z0-9"] @@ -91,8 +98,6 @@ WC [\200-\377_A-Za-z0-9"] \.\. { return K_DOTDOT; } alias { return K_ALIAS; } begin { return K_BEGIN; } -bpchar { return T_BPCHAR; } -char { return T_CHAR; } close { return K_CLOSE; } constant { return K_CONSTANT; } cursor { return K_CURSOR; } @@ -131,7 +136,6 @@ select { return K_SELECT; } then { return K_THEN; } to { return K_TO; } type { return K_TYPE; } -varchar { return T_VARCHAR; } when { return K_WHEN; } while { return K_WHILE; } @@ -235,7 +239,37 @@ plpgsql_input(char *buf, int *result, int max) plpgsql_bytes_left -= n; } +/* + * This is the yylex routine called from outside. It exists to provide + * a token pushback facility. + */ +int +plpgsql_yylex(void) +{ + if (have_pushback_token) + { + have_pushback_token = false; + return pushback_token; + } + return yylex(); +} +/* + * Push back a single token to be re-read by next plpgsql_yylex() call. + */ +void +plpgsql_push_back_token(int token) +{ + if (have_pushback_token) + elog(ERROR, "plpgsql_push_back_token: can't push back multiple tokens"); + pushback_token = token; + have_pushback_token = true; +} + + +/* + * Initialize the scanner for new input. + */ void plpgsql_setinput(char *source, int functype) { @@ -261,4 +295,6 @@ plpgsql_setinput(char *source, int functype) scanner_functype = functype; scanner_typereported = 0; + + have_pushback_token = false; }