From 0b3bca6c6f92722c9c4dc8ae69703b8a5cff363f Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 9 Oct 2001 04:15:38 +0000
Subject: [PATCH] Change plpgsql to depend on main parser's type-declaration
 grammar, rather than having its own somewhat half-baked notion of what a type
 declaration looks like.  This is necessary now to ensure that plpgsql will
 think a 'timestamp' variable has the same semantics as 'timestamp' does in
 the main SQL grammar; and it should avoid divergences in future.

---
 doc/src/sgml/plsql.sgml         |  13 ++-
 src/backend/parser/parse_type.c |  75 ++++++++++++++++-
 src/include/parser/parse_type.h |   4 +-
 src/pl/plpgsql/src/Makefile     |  16 ++--
 src/pl/plpgsql/src/gram.y       | 145 +++++++++++++++++---------------
 src/pl/plpgsql/src/pl_comp.c    | 141 +++++++++++++------------------
 src/pl/plpgsql/src/plpgsql.h    |  17 ++--
 src/pl/plpgsql/src/scan.l       |  48 +++++++++--
 8 files changed, 281 insertions(+), 178 deletions(-)

diff --git a/doc/src/sgml/plsql.sgml b/doc/src/sgml/plsql.sgml
index a2d2395f915..d7f1b99d1b4 100644
--- a/doc/src/sgml/plsql.sgml
+++ b/doc/src/sgml/plsql.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/Attic/plsql.sgml,v 2.40 2001/09/18 12:08:26 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/Attic/plsql.sgml,v 2.41 2001/10/09 04:15:38 tgl Exp $
 -->
 
 <chapter id="plpgsql"> 
@@ -229,7 +229,7 @@ END;
     re-create them. For example:
 <programlisting>
 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:
 <programlisting>
 user_id INTEGER;
-quantity NUMBER(5);
+quantity NUMERIC(5);
 url VARCHAR;
 </programlisting>
     </para>
@@ -437,7 +437,7 @@ END;
      <para>
       Using the <type>%TYPE</type> and <type>%ROWTYPE</type>
       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).
      </para>
 
@@ -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 <replaceable>expression</replaceable>
      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 <application>PL/pgSQL</application> 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.
     </para>
 
     <para>
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 856affbdbe3..ed010a46ed0 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 dee44cfe10a..baa31c972b6 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 bc62d95a011..914b57d1153 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 ad45bd2d49b..a2d37c3f996 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 <stdio.h>
-#include <string.h>
 #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 <declhdr> decl_sect
 %type <varname> decl_varname
 %type <str>		decl_renname
-%type <ival>	decl_const, decl_notnull, decl_atttypmod, decl_atttypmodval
+%type <ival>	decl_const, decl_notnull
 %type <expr>	decl_defval, decl_cursor_query
-%type <dtype>	decl_datatype, decl_dtypename
+%type <dtype>	decl_datatype
 %type <row>		decl_rowtype, decl_cursor_args, decl_cursor_arglist
 %type <nsitem>	decl_aliasitem
 %type <str>		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
-					{
-						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
+decl_datatype	:
 					{
-						$$ = 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 5c7e12ad198..c06a2c87811 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 <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
+#include "plpgsql.h"
+
 #include <unistd.h>
 #include <fcntl.h>
-#include <string.h>
 #include <ctype.h>
 
-#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 c5942fa0698..fc2032dbd6d 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_yyparse(void);
+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);
-extern int	plpgsql_yyparse(void);
-extern void plpgsql_yyerror(const char *s);
 
 #endif	 /* PLPGSQL_H */
diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l
index 6e17365705c..dfb16f81771 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;
 }
-- 
GitLab