diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1fa9f19f08..9e7f1590b5 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12095,7 +12095,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
 	 * parse_analyze() or the rewriter, but instead we need to pass them
 	 * through parse_utilcmd.c to make them ready for execution.
 	 */
-	raw_parsetree_list = raw_parser(cmd);
+	raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
 	querytree_list = NIL;
 	foreach(list_item, raw_parsetree_list)
 	{
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8f341ac006..88c76dd985 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -723,6 +723,15 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  */
 %token		NOT_LA NULLS_LA WITH_LA
 
+/*
+ * The grammar likewise thinks these tokens are keywords, but they are never
+ * generated by the scanner.  Rather, they can be injected by parser.c as
+ * the initial token of the string (using the lookahead-token mechanism
+ * implemented there).  This provides a way to tell the grammar to parse
+ * something other than the usual list of SQL commands.
+ */
+%token		MODE_TYPE_NAME
+
 
 /* Precedence: lowest to highest */
 %nonassoc	SET				/* see relation_expr_opt_alias */
@@ -787,11 +796,18 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 /*
  *	The target production for the whole parse.
+ *
+ * Ordinarily we parse a list of statements, but if we see one of the
+ * special MODE_XXX symbols as first token, we parse something else.
  */
 stmtblock:	stmtmulti
 			{
 				pg_yyget_extra(yyscanner)->parsetree = $1;
 			}
+			| MODE_TYPE_NAME Typename
+			{
+				pg_yyget_extra(yyscanner)->parsetree = list_make1($2);
+			}
 		;
 
 /*
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 2709f6f9c7..633774b733 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -719,13 +719,6 @@ pts_error_callback(void *arg)
 	const char *str = (const char *) arg;
 
 	errcontext("invalid type name \"%s\"", str);
-
-	/*
-	 * Currently we just suppress any syntax error position report, rather
-	 * than transforming to an "internal query" error.  It's unlikely that a
-	 * type name is complex enough to need positioning.
-	 */
-	errposition(0);
 }
 
 /*
@@ -737,11 +730,7 @@ pts_error_callback(void *arg)
 TypeName *
 typeStringToTypeName(const char *str)
 {
-	StringInfoData buf;
 	List	   *raw_parsetree_list;
-	SelectStmt *stmt;
-	ResTarget  *restarget;
-	TypeCast   *typecast;
 	TypeName   *typeName;
 	ErrorContextCallback ptserrcontext;
 
@@ -749,9 +738,6 @@ typeStringToTypeName(const char *str)
 	if (strspn(str, " \t\n\r\f") == strlen(str))
 		goto fail;
 
-	initStringInfo(&buf);
-	appendStringInfo(&buf, "SELECT NULL::%s", str);
-
 	/*
 	 * Setup error traceback support in case of ereport() during parse
 	 */
@@ -760,58 +746,18 @@ typeStringToTypeName(const char *str)
 	ptserrcontext.previous = error_context_stack;
 	error_context_stack = &ptserrcontext;
 
-	raw_parsetree_list = raw_parser(buf.data);
+	raw_parsetree_list = raw_parser(str, RAW_PARSE_TYPE_NAME);
 
 	error_context_stack = ptserrcontext.previous;
 
-	/*
-	 * Make sure we got back exactly what we expected and no more; paranoia is
-	 * justified since the string might contain anything.
-	 */
-	if (list_length(raw_parsetree_list) != 1)
-		goto fail;
-	stmt = (SelectStmt *) linitial_node(RawStmt, raw_parsetree_list)->stmt;
-	if (stmt == NULL ||
-		!IsA(stmt, SelectStmt) ||
-		stmt->distinctClause != NIL ||
-		stmt->intoClause != NULL ||
-		stmt->fromClause != NIL ||
-		stmt->whereClause != NULL ||
-		stmt->groupClause != NIL ||
-		stmt->havingClause != NULL ||
-		stmt->windowClause != NIL ||
-		stmt->valuesLists != NIL ||
-		stmt->sortClause != NIL ||
-		stmt->limitOffset != NULL ||
-		stmt->limitCount != NULL ||
-		stmt->lockingClause != NIL ||
-		stmt->withClause != NULL ||
-		stmt->op != SETOP_NONE)
-		goto fail;
-	if (list_length(stmt->targetList) != 1)
-		goto fail;
-	restarget = (ResTarget *) linitial(stmt->targetList);
-	if (restarget == NULL ||
-		!IsA(restarget, ResTarget) ||
-		restarget->name != NULL ||
-		restarget->indirection != NIL)
-		goto fail;
-	typecast = (TypeCast *) restarget->val;
-	if (typecast == NULL ||
-		!IsA(typecast, TypeCast) ||
-		typecast->arg == NULL ||
-		!IsA(typecast->arg, A_Const))
-		goto fail;
+	/* We should get back exactly one TypeName node. */
+	Assert(list_length(raw_parsetree_list) == 1);
+	typeName = linitial_node(TypeName, raw_parsetree_list);
 
-	typeName = typecast->typeName;
-	if (typeName == NULL ||
-		!IsA(typeName, TypeName))
-		goto fail;
+	/* The grammar allows SETOF in TypeName, but we don't want that here. */
 	if (typeName->setof)
 		goto fail;
 
-	pfree(buf.data);
-
 	return typeName;
 
 fail:
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index be86eb37fe..b04be3bca5 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -35,11 +35,11 @@ static char *str_udeescape(const char *str, char escape,
  * raw_parser
  *		Given a query in string form, do lexical and grammatical analysis.
  *
- * Returns a list of raw (un-analyzed) parse trees.  The immediate elements
- * of the list are always RawStmt nodes.
+ * Returns a list of raw (un-analyzed) parse trees.  The contents of the
+ * list have the form required by the specified RawParseMode.
  */
 List *
-raw_parser(const char *str)
+raw_parser(const char *str, RawParseMode mode)
 {
 	core_yyscan_t yyscanner;
 	base_yy_extra_type yyextra;
@@ -49,8 +49,22 @@ raw_parser(const char *str)
 	yyscanner = scanner_init(str, &yyextra.core_yy_extra,
 							 &ScanKeywords, ScanKeywordTokens);
 
-	/* base_yylex() only needs this much initialization */
-	yyextra.have_lookahead = false;
+	/* base_yylex() only needs us to initialize the lookahead token, if any */
+	if (mode == RAW_PARSE_DEFAULT)
+		yyextra.have_lookahead = false;
+	else
+	{
+		/* this array is indexed by RawParseMode enum */
+		static const int mode_token[] = {
+			0,					/* RAW_PARSE_DEFAULT */
+			MODE_TYPE_NAME		/* RAW_PARSE_TYPE_NAME */
+		};
+
+		yyextra.have_lookahead = true;
+		yyextra.lookahead_token = mode_token[mode];
+		yyextra.lookahead_yylloc = 0;
+		yyextra.lookahead_end = NULL;
+	}
 
 	/* initialize the bison parser */
 	parser_init(&yyextra);
@@ -104,7 +118,8 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
 		cur_token = yyextra->lookahead_token;
 		lvalp->core_yystype = yyextra->lookahead_yylval;
 		*llocp = yyextra->lookahead_yylloc;
-		*(yyextra->lookahead_end) = yyextra->lookahead_hold_char;
+		if (yyextra->lookahead_end)
+			*(yyextra->lookahead_end) = yyextra->lookahead_hold_char;
 		yyextra->have_lookahead = false;
 	}
 	else
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3679799e50..64c06f615b 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -634,7 +634,7 @@ pg_parse_query(const char *query_string)
 	if (log_parser_stats)
 		ResetUsage();
 
-	raw_parsetree_list = raw_parser(query_string);
+	raw_parsetree_list = raw_parser(query_string, RAW_PARSE_DEFAULT);
 
 	if (log_parser_stats)
 		ShowUsage("PARSER STATISTICS");
diff --git a/src/include/parser/parser.h b/src/include/parser/parser.h
index 3bdeeb8b0b..ebbcb788cc 100644
--- a/src/include/parser/parser.h
+++ b/src/include/parser/parser.h
@@ -18,6 +18,24 @@
 #include "nodes/parsenodes.h"
 
 
+/*
+ * RawParseMode determines the form of the string that raw_parser() accepts:
+ *
+ * RAW_PARSE_DEFAULT: parse a semicolon-separated list of SQL commands,
+ * and return a List of RawStmt nodes.
+ *
+ * RAW_PARSE_TYPE_NAME: parse a type name, and return a one-element List
+ * containing a TypeName node.
+ *
+ * ... more to come ...
+ */
+typedef enum
+{
+	RAW_PARSE_DEFAULT = 0,
+	RAW_PARSE_TYPE_NAME
+} RawParseMode;
+
+/* Values for the backslash_quote GUC */
 typedef enum
 {
 	BACKSLASH_QUOTE_OFF,
@@ -32,7 +50,7 @@ extern PGDLLIMPORT bool standard_conforming_strings;
 
 
 /* Primary entry point for the raw parsing functions */
-extern List *raw_parser(const char *str);
+extern List *raw_parser(const char *str, RawParseMode mode);
 
 /* Utility functions exported by gram.y (perhaps these should be elsewhere) */
 extern List *SystemFuncName(char *name);
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 8227bf0449..26732d875e 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -3661,7 +3661,7 @@ check_sql_expr(const char *stmt, int location, int leaderlen)
 	error_context_stack = &syntax_errcontext;
 
 	oldCxt = MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
-	(void) raw_parser(stmt);
+	(void) raw_parser(stmt, RAW_PARSE_DEFAULT);
 	MemoryContextSwitchTo(oldCxt);
 
 	/* Restore former ereport callback */
