Pass ParseState as down to utility functions.

Started by Kirill Reshkeabout 1 year ago22 messages
#1Kirill Reshke
reshkekirill@gmail.com
1 attachment(s)

Hi hackers!
PFA patch fixing a number of places where typenameType called with NULL pstate.

=== motivation.

Per discussion in a nearby thread for `CREATE SCHEMA ... CREATE DOMAIN
support`. Suggested by Jian He & Tom Lane.

On Thu, 28 Nov 2024 at 10:52, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Kirill Reshke <reshkekirill@gmail.com> writes:

On Wed, 27 Nov 2024 at 23:39, Tom Lane <tgl@sss.pgh.pa.us> wrote:

We've fixed a few utility statements so that they can receive
a passed-down ParseState, but not DefineDomain.

PFA as an independent patch then. Or should we combine these two into one?

No, I don't think this should be part of the patch discussed in this
thread.

It feels rather random to me to be fixing only DefineDomain;
I'm sure there's more in the same vein. I'd like to see a
patch with a scope along the lines of "fix everything reachable
within CREATE SCHEMA" or perhaps "fix all calls of typenameType".
(A quick grep shows that an outright majority of the callers of that
are passing null ParseState. I didn't look to see if any of them
have a good excuse beyond "we didn't do the plumbing work".)

regards, tom lane

I chosed "fix all calls of typenameType" way.

I searched for typenameType(NULL pattern within sources and changed to
pass pstate where appropriate. This is AlterType, DefineDomain and
transformOfType cases. There are 2 more usages of this pattern left,
inside ATExecAlterColumnType & ATExecAddOf, which I dont think need to
be addressed (cure worse than disease).

=== examples
1) CREATE TYPE.
before:

```
db2=# create type int8alias3 (
input = int8alias2in,
output = int8alias2out,
like = int82
);
ERROR: type "int82" does not exist
db2=# ^C
```

after:

```
db2=# create type int8alias3 (
input = int8alias2in,
output = int8alias2out,
like = int82
);
ERROR: type "int82" does not exist
LINE 4: like = int82
^
db2=#
```

2) TABLE of TYPENAME case

before:

```
db2=# CREATE TABLE example OF mytype2 (PRIMARY KEY (some_id));
ERROR: type "mytype2" does not exist
db2=#

```

after:

```
db2=# CREATE TABLE example OF mytype2 (PRIMARY KEY (some_id));
ERROR: type "mytype2" does not exist
LINE 1: CREATE TABLE example OF mytype2 (PRIMARY KEY (some_id));
^
```

3) CREATE DOMAIN - analogous.

==== testing

By-hand. Let me know if we can check this any other way.

--
Best regards,
Kirill Reshke

Attachments:

v2-0001-Pass-ParseState-as-down-to-utility-functions.patchapplication/octet-stream; name=v2-0001-Pass-ParseState-as-down-to-utility-functions.patchDownload
From 61c1f58c3f43f61b9add912f62de7173bb9fef1c Mon Sep 17 00:00:00 2001
From: reshke kirill <reshke@double.cloud>
Date: Thu, 28 Nov 2024 05:19:58 +0000
Subject: [PATCH v2] Pass ParseState as down to utility functions.

ParseState was lacking in a number of places, for example
typenameType call inside DefineDomain.
This patch fixes that. Now error-message for create domain
also show error location.
---
 src/backend/commands/typecmds.c    | 14 +++++---------
 src/backend/parser/parse_utilcmd.c |  2 +-
 src/backend/tcop/utility.c         |  4 ++--
 src/include/commands/typecmds.h    |  4 ++--
 4 files changed, 10 insertions(+), 14 deletions(-)

diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 859e2191f08..11548da1fd5 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -348,7 +348,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 		Type		likeType;
 		Form_pg_type likeForm;
 
-		likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+		likeType = typenameType(pstate, defGetTypeName(likeTypeEl), NULL);
 		likeForm = (Form_pg_type) GETSTRUCT(likeType);
 		internalLength = likeForm->typlen;
 		byValue = likeForm->typbyval;
@@ -694,7 +694,7 @@ RemoveTypeById(Oid typeOid)
  *		Registers a new domain.
  */
 ObjectAddress
-DefineDomain(CreateDomainStmt *stmt)
+DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
 {
 	char	   *domainName;
 	char	   *domainArrayName;
@@ -761,7 +761,7 @@ DefineDomain(CreateDomainStmt *stmt)
 	/*
 	 * Look up the base type.
 	 */
-	typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
+	typeTup = typenameType(pstate, stmt->typeName, &basetypeMod);
 	baseType = (Form_pg_type) GETSTRUCT(typeTup);
 	basetypeoid = baseType->oid;
 
@@ -885,12 +885,8 @@ DefineDomain(CreateDomainStmt *stmt)
 
 				if (constr->raw_expr)
 				{
-					ParseState *pstate;
 					Node	   *defaultExpr;
 
-					/* Create a dummy ParseState for transformExpr */
-					pstate = make_parsestate(NULL);
-
 					/*
 					 * Cook the constr->raw_expr into an expression. Note:
 					 * name is strictly for error message
@@ -4315,7 +4311,7 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
  * adding new flexibility.
  */
 ObjectAddress
-AlterType(AlterTypeStmt *stmt)
+AlterType(ParseState *pstate, AlterTypeStmt *stmt)
 {
 	ObjectAddress address;
 	Relation	catalog;
@@ -4331,7 +4327,7 @@ AlterType(AlterTypeStmt *stmt)
 
 	/* Make a TypeName so we can use standard type lookup machinery */
 	typename = makeTypeNameFromNameList(stmt->typeName);
-	tup = typenameType(NULL, typename, NULL);
+	tup = typenameType(pstate, typename, NULL);
 
 	typeOid = typeTypeId(tup);
 	typForm = (Form_pg_type) GETSTRUCT(tup);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0f324ee4e31..95dad766834 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1615,7 +1615,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
 
 	Assert(ofTypename);
 
-	tuple = typenameType(NULL, ofTypename, NULL);
+	tuple = typenameType(cxt->pstate, ofTypename, NULL);
 	check_of_type(tuple);
 	ofTypeId = ((Form_pg_type) GETSTRUCT(tuple))->oid;
 	ofTypename->typeOid = ofTypeId; /* cached for later */
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f28bf371059..aa24283efff 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1712,7 +1712,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_CreateDomainStmt:
-				address = DefineDomain((CreateDomainStmt *) parsetree);
+				address = DefineDomain(pstate, (CreateDomainStmt *) parsetree);
 				break;
 
 			case T_CreateConversionStmt:
@@ -1801,7 +1801,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_AlterTypeStmt:
-				address = AlterType((AlterTypeStmt *) parsetree);
+				address = AlterType(pstate, (AlterTypeStmt *) parsetree);
 				break;
 
 			case T_CommentStmt:
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index e1b02927c4b..6e493b896c2 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -23,7 +23,7 @@
 
 extern ObjectAddress DefineType(ParseState *pstate, List *names, List *parameters);
 extern void RemoveTypeById(Oid typeOid);
-extern ObjectAddress DefineDomain(CreateDomainStmt *stmt);
+extern ObjectAddress DefineDomain(ParseState *pstate, CreateDomainStmt *stmt);
 extern ObjectAddress DefineEnum(CreateEnumStmt *stmt);
 extern ObjectAddress DefineRange(ParseState *pstate, CreateRangeStmt *stmt);
 extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
@@ -58,6 +58,6 @@ extern Oid	AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 									   bool errorOnTableType,
 									   ObjectAddresses *objsMoved);
 
-extern ObjectAddress AlterType(AlterTypeStmt *stmt);
+extern ObjectAddress AlterType(ParseState *pstate, AlterTypeStmt *stmt);
 
 #endif							/* TYPECMDS_H */
-- 
2.34.1

#2jian he
jian.universality@gmail.com
In reply to: Kirill Reshke (#1)
1 attachment(s)
Re: Pass ParseState as down to utility functions.

hi.

ATExecAddOf
DefineType
ATPrepAlterColumnType
ATExecAlterColumnType
DefineDomain
AlterType
i changed the above function, so the above related function errors may
print out error position.
reason for change mainly because these functions have
`typenameType(NULL, typeName, &targettypmod);`
we want to pass not NULL pstate (typenameType(pstate, typeName, &targettypmod);)

why do we want printout error position
1. it can quickly locate DDL command error positions, beginner friendly.
2. in the thread `CREATE SCHEMA ... CREATE DOMAIN support`, case like:
CREATE SCHEMA regress_schema_2
create domain ss1 as ss
create domain ss as text;
ERROR: type "ss" does not exist
obviously the error is not helpful at all.

As you can see, in cases like a single DDL, multiple sub DDL within
it, error position is quite important
I also added some tests for DefineDomain.
added parser_errposition for many places in DefineDomain.

the attached patch (based on Kirill Reshke 's v2 patch)
either passing the source_string to the existing ParseState
or by making a new ParseState passing source_string to it.
then add
`parser_errposition(pstate, location)))`
in various places optionally.

So I guess bundling it into a single patch should be fine?

Attachments:

v3-0001-print-out-error-position-for-some-DDL-command.patchtext/x-patch; charset=US-ASCII; name=v3-0001-print-out-error-position-for-some-DDL-command.patchDownload
From 181b362c66a124721c56ddd461e44158283e6548 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Sat, 30 Nov 2024 14:44:38 +0800
Subject: [PATCH v3 1/1] print out error position for some DDL command.

doing this by passing the source_string to the existing ParseState
or by making a new ParseState passing source_string to it.

With this patch, the following functions will printout the error position for certain error cases.

ATExecAddOf
DefineType
ATPrepAlterColumnType
ATExecAlterColumnType
DefineDomain
AlterType

This can be particularly helpful when working with a sequence of DML commands,
such as `create schema create schema_element`.
It also makes it easier to quickly identify the relevant error area in a single DDL command.
---
 src/backend/commands/tablecmds.c          | 43 +++++++++++++-------
 src/backend/commands/typecmds.c           | 49 +++++++++++++----------
 src/backend/parser/parse_utilcmd.c        |  2 +-
 src/backend/tcop/utility.c                |  4 +-
 src/include/commands/typecmds.h           |  4 +-
 src/test/regress/expected/alter_table.out | 13 ++++++
 src/test/regress/expected/domain.out      | 17 ++++++++
 src/test/regress/sql/alter_table.sql      |  5 +++
 src/test/regress/sql/domain.sql           |  5 +++
 9 files changed, 102 insertions(+), 40 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6ccae4cb4a..efa38b1470 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -593,7 +593,8 @@ static void ATPrepAlterColumnType(List **wqueue,
 								  AlterTableUtilityContext *context);
 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-										   AlterTableCmd *cmd, LOCKMODE lockmode);
+										   AlterTableCmd *cmd, LOCKMODE lockmode,
+										   AlterTableUtilityContext *context);
 static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
 											  Relation rel, AttrNumber attnum, const char *colName);
 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
@@ -639,7 +640,9 @@ static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCK
 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
 								   DependencyType deptype);
-static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
+static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename,
+								 LOCKMODE lockmode,
+								 AlterTableUtilityContext *context);
 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
 static void ATExecGenericOptions(Relation rel, List *options);
@@ -5413,7 +5416,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			break;
 		case AT_AlterColumnType:	/* ALTER COLUMN TYPE */
 			/* parse transformation was done earlier */
-			address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
+			address = ATExecAlterColumnType(tab, rel, cmd, lockmode, context);
 			break;
 		case AT_AlterColumnGenericOptions:	/* ALTER COLUMN OPTIONS */
 			address =
@@ -5537,7 +5540,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
 			break;
 		case AT_AddOf:
-			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
+			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode, context);
 			break;
 		case AT_DropOf:
 			ATExecDropOf(rel, lockmode);
@@ -13218,10 +13221,12 @@ ATPrepAlterColumnType(List **wqueue,
 	AclResult	aclresult;
 	bool		is_expr;
 
+	pstate->p_sourcetext = context->queryString;
 	if (rel->rd_rel->reloftype && !recursing)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("cannot alter column type of typed table")));
+				 errmsg("cannot alter column type of typed table"),
+				 parser_errposition(pstate, def->location)));
 
 	/* lookup the attribute so we can check inheritance status */
 	tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
@@ -13237,8 +13242,8 @@ ATPrepAlterColumnType(List **wqueue,
 	if (attnum <= 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot alter system column \"%s\"",
-						colName)));
+				 errmsg("cannot alter system column \"%s\"",colName),
+				 parser_errposition(pstate, def->location)));
 
 	/*
 	 * Cannot specify USING when altering type of a generated column, because
@@ -13271,14 +13276,14 @@ ATPrepAlterColumnType(List **wqueue,
 						colName, RelationGetRelationName(rel))));
 
 	/* Look up the target type */
-	typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
+	typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
 
 	aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error_type(aclresult, targettype);
 
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colName, targettype, targetcollid,
@@ -13537,7 +13542,8 @@ ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
  */
 static ObjectAddress
 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-					  AlterTableCmd *cmd, LOCKMODE lockmode)
+					  AlterTableCmd *cmd, LOCKMODE lockmode,
+					  AlterTableUtilityContext *context)
 {
 	char	   *colName = cmd->name;
 	ColumnDef  *def = (ColumnDef *) cmd->def;
@@ -13558,6 +13564,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	SysScanDesc scan;
 	HeapTuple	depTup;
 	ObjectAddress address;
+	ParseState      *pstate;
+
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
 
 	/*
 	 * Clear all the missing values if we're rewriting the table, since this
@@ -13596,11 +13606,11 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 						colName)));
 
 	/* Look up the target type (should not fail, since prep found it) */
-	typeTuple = typenameType(NULL, typeName, &targettypmod);
+	typeTuple = typenameType(pstate, typeName, &targettypmod);
 	tform = (Form_pg_type) GETSTRUCT(typeTuple);
 	targettype = tform->oid;
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/*
 	 * If there is a default expression for the column, get it and ensure we
@@ -16976,7 +16986,8 @@ drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
  * The address of the type is returned.
  */
 static ObjectAddress
-ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
+ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode,
+			AlterTableUtilityContext *context)
 {
 	Oid			relid = RelationGetRelid(rel);
 	Type		typetuple;
@@ -16993,9 +17004,13 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 	ObjectAddress tableobj,
 				typeobj;
 	HeapTuple	classtuple;
+	ParseState 	*pstate;
 
 	/* Validate the type. */
-	typetuple = typenameType(NULL, ofTypename, NULL);
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
+
+	typetuple = typenameType(pstate, ofTypename, NULL);
 	check_of_type(typetuple);
 	typeform = (Form_pg_type) GETSTRUCT(typetuple);
 	typeid = typeform->oid;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 971a8a1ebc..0d0626d1ff 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -348,7 +348,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 		Type		likeType;
 		Form_pg_type likeForm;
 
-		likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+		likeType = typenameType(pstate, defGetTypeName(likeTypeEl), NULL);
 		likeForm = (Form_pg_type) GETSTRUCT(likeType);
 		internalLength = likeForm->typlen;
 		byValue = likeForm->typbyval;
@@ -694,7 +694,7 @@ RemoveTypeById(Oid typeOid)
  *		Registers a new domain.
  */
 ObjectAddress
-DefineDomain(CreateDomainStmt *stmt)
+DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
 {
 	char	   *domainName;
 	char	   *domainArrayName;
@@ -761,7 +761,7 @@ DefineDomain(CreateDomainStmt *stmt)
 	/*
 	 * Look up the base type.
 	 */
-	typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
+	typeTup = typenameType(pstate, stmt->typeName, &basetypeMod);
 	baseType = (Form_pg_type) GETSTRUCT(typeTup);
 	basetypeoid = baseType->oid;
 
@@ -783,7 +783,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("\"%s\" is not a valid base type for a domain",
-						TypeNameToString(stmt->typeName))));
+						TypeNameToString(stmt->typeName)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	aclresult = object_aclcheck(TypeRelationId, basetypeoid, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
@@ -809,7 +810,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("collations are not supported by type %s",
-						format_type_be(basetypeoid))));
+						format_type_be(basetypeoid)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	/* passed by value */
 	byValue = baseType->typbyval;
@@ -880,17 +882,14 @@ DefineDomain(CreateDomainStmt *stmt)
 				if (saw_default)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("multiple default expressions")));
+							 errmsg("multiple default expressions"),
+							 parser_errposition(pstate, constr->location)));
 				saw_default = true;
 
 				if (constr->raw_expr)
 				{
-					ParseState *pstate;
 					Node	   *defaultExpr;
 
-					/* Create a dummy ParseState for transformExpr */
-					pstate = make_parsestate(NULL);
-
 					/*
 					 * Cook the constr->raw_expr into an expression. Note:
 					 * name is strictly for error message
@@ -943,11 +942,13 @@ DefineDomain(CreateDomainStmt *stmt)
 				if (nullDefined && !typNotNull)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							 errmsg("conflicting NULL/NOT NULL constraints"),
+							 parser_errposition(pstate, constr->location)));
 				if (constr->is_no_inherit)
 					ereport(ERROR,
-							errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							errmsg("not-null constraints for domains cannot be marked NO INHERIT"));
+							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+							 errmsg("not-null constraints for domains cannot be marked NO INHERIT"),
+							 parser_errposition(pstate, constr->location)));
 				typNotNull = true;
 				nullDefined = true;
 				break;
@@ -956,7 +957,8 @@ DefineDomain(CreateDomainStmt *stmt)
 				if (nullDefined && typNotNull)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							 errmsg("conflicting NULL/NOT NULL constraints"),
+							 parser_errposition(pstate, constr->location)));
 				typNotNull = false;
 				nullDefined = true;
 				break;
@@ -981,25 +983,29 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_UNIQUE:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("unique constraints not possible for domains")));
+						 errmsg("unique constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_PRIMARY:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("primary key constraints not possible for domains")));
+						 errmsg("primary key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_EXCLUSION:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("exclusion constraints not possible for domains")));
+						 errmsg("exclusion constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_FOREIGN:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("foreign key constraints not possible for domains")));
+						 errmsg("foreign key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_ATTR_DEFERRABLE:
@@ -1008,7 +1014,8 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_ATTR_IMMEDIATE:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("specifying constraint deferrability not supported for domains")));
+						 errmsg("specifying constraint deferrability not supported for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			default:
@@ -4315,7 +4322,7 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
  * adding new flexibility.
  */
 ObjectAddress
-AlterType(AlterTypeStmt *stmt)
+AlterType(ParseState *pstate, AlterTypeStmt *stmt)
 {
 	ObjectAddress address;
 	Relation	catalog;
@@ -4331,7 +4338,7 @@ AlterType(AlterTypeStmt *stmt)
 
 	/* Make a TypeName so we can use standard type lookup machinery */
 	typename = makeTypeNameFromNameList(stmt->typeName);
-	tup = typenameType(NULL, typename, NULL);
+	tup = typenameType(pstate, typename, NULL);
 
 	typeOid = typeTypeId(tup);
 	typForm = (Form_pg_type) GETSTRUCT(tup);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0f324ee4e3..95dad76683 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1615,7 +1615,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
 
 	Assert(ofTypename);
 
-	tuple = typenameType(NULL, ofTypename, NULL);
+	tuple = typenameType(cxt->pstate, ofTypename, NULL);
 	check_of_type(tuple);
 	ofTypeId = ((Form_pg_type) GETSTRUCT(tuple))->oid;
 	ofTypename->typeOid = ofTypeId; /* cached for later */
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f28bf37105..aa24283eff 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1712,7 +1712,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_CreateDomainStmt:
-				address = DefineDomain((CreateDomainStmt *) parsetree);
+				address = DefineDomain(pstate, (CreateDomainStmt *) parsetree);
 				break;
 
 			case T_CreateConversionStmt:
@@ -1801,7 +1801,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_AlterTypeStmt:
-				address = AlterType((AlterTypeStmt *) parsetree);
+				address = AlterType(pstate, (AlterTypeStmt *) parsetree);
 				break;
 
 			case T_CommentStmt:
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index e1b02927c4..6e493b896c 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -23,7 +23,7 @@
 
 extern ObjectAddress DefineType(ParseState *pstate, List *names, List *parameters);
 extern void RemoveTypeById(Oid typeOid);
-extern ObjectAddress DefineDomain(CreateDomainStmt *stmt);
+extern ObjectAddress DefineDomain(ParseState *pstate, CreateDomainStmt *stmt);
 extern ObjectAddress DefineEnum(CreateEnumStmt *stmt);
 extern ObjectAddress DefineRange(ParseState *pstate, CreateRangeStmt *stmt);
 extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
@@ -58,6 +58,6 @@ extern Oid	AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 									   bool errorOnTableType,
 									   ObjectAddresses *objsMoved);
 
-extern ObjectAddress AlterType(AlterTypeStmt *stmt);
+extern ObjectAddress AlterType(ParseState *pstate, AlterTypeStmt *stmt);
 
 #endif							/* TYPECMDS_H */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 2212c8dbb5..ebcac07063 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3413,6 +3413,19 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ERROR:  cannot alter system column "xmin"
+LINE 1: ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+                                              ^
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ERROR:  type "x" does not exist
+LINE 1: ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+                                                               ^
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+ERROR:  collations are not supported by type integer
+LINE 1: ...LE comment_test ALTER COLUMN id SET DATA TYPE int collate "C...
+                                                             ^
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
            comment           
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 42b6559f9c..fcc50d0f89 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -15,6 +15,23 @@ NOTICE:  drop cascades to type dependenttypetest
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 ERROR:  type "domaindroptest" does not exist
+--test syntax.
+create domain d_fail  as int default 1 default 2;
+ERROR:  multiple default expressions
+LINE 1: create domain d_fail  as int default 1 default 2;
+                                               ^
+create domain d_fail int4 DEFAULT 3 + 'h';
+ERROR:  invalid input syntax for type integer: "h"
+LINE 1: create domain d_fail int4 DEFAULT 3 + 'h';
+                                              ^
+create domain d_fail int4 collate "C";
+ERROR:  collations are not supported by type integer
+LINE 1: create domain d_fail int4 collate "C";
+                             ^
+create domain d_fail as anyelement;
+ERROR:  "anyelement" is not a valid base type for a domain
+LINE 1: create domain d_fail as anyelement;
+                                ^
 -- Test domain input.
 -- Note: the point of checking both INSERT and COPY FROM is that INSERT
 -- exercises CoerceToDomain while COPY exercises domain_in.
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 637e3dac38..f7d45932ae 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2145,6 +2145,11 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
 
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
 SELECT indexrelid::regclass::text as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass ORDER BY 1, 2;
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index ee07b03174..0966b8570b 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -16,6 +16,11 @@ drop domain domaindroptest cascade;
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 
+--test syntax.
+create domain d_fail  as int default 1 default 2;
+create domain d_fail int4 DEFAULT 3 + 'h';
+create domain d_fail int4 collate "C";
+create domain d_fail as anyelement;
 
 -- Test domain input.
 
-- 
2.34.1

#3Kirill Reshke
reshkekirill@gmail.com
In reply to: jian he (#2)
Re: Pass ParseState as down to utility functions.

On Sat, 30 Nov 2024 at 17:37, jian he <jian.universality@gmail.com> wrote:

So I guess bundling it into a single patch should be fine?

Ok. I created CF entry for this patch.

[0]: https://commitfest.postgresql.org/51/5420/ -- Best regards, Kirill Reshke
--
Best regards,
Kirill Reshke

#4Michael Paquier
michael@paquier.xyz
In reply to: Kirill Reshke (#3)
Re: Pass ParseState as down to utility functions.

On Wed, Dec 04, 2024 at 03:31:47PM +0500, Kirill Reshke wrote:

On Sat, 30 Nov 2024 at 17:37, jian he <jian.universality@gmail.com> wrote:

So I guess bundling it into a single patch should be fine?

Ok. I created CF entry for this patch.

[0] https://commitfest.postgresql.org/51/5420/

Note that v3 of the patch is failing in the CI, so you should look at
that:
https://cirrus-ci.com/github/postgresql-cfbot/postgresql/cf%2F5420

Combining everything into a single patch is not a big deal in this
case IMO as the code paths touched are different.

I was playing with the patch and tried how typenameType() would like
to force a rule so as the pstate should be always non-NULL, and got
reminded by the various callers of typenameTypeIdAndMod() that this
was a bad idea.

This patch does not show how the error reports are influenced for
DefineType() and AlterType().

This reminds as well that there is little coverage for many error
paths of DefineDomain(), with some paths actually modified in this
patch:
https://coverage.postgresql.org/src/backend/commands/typecmds.c.gcov.html
I'd suggest to do something about that, while on it, to check that the
parser issues a location on error.
--
Michael

#5Kirill Reshke
reshkekirill@gmail.com
In reply to: Michael Paquier (#4)
1 attachment(s)
Re: Pass ParseState as down to utility functions.

On Thu, 5 Dec 2024 at 11:45, Michael Paquier <michael@paquier.xyz> wrote:

On Wed, Dec 04, 2024 at 03:31:47PM +0500, Kirill Reshke wrote:

On Sat, 30 Nov 2024 at 17:37, jian he <jian.universality@gmail.com> wrote:

So I guess bundling it into a single patch should be fine?

Ok. I created CF entry for this patch.

[0] https://commitfest.postgresql.org/51/5420/

Note that v3 of the patch is failing in the CI, so you should look at
that:
https://cirrus-ci.com/github/postgresql-cfbot/postgresql/cf%2F5420

Indeed. Thank you.

Combining everything into a single patch is not a big deal in this
case IMO as the code paths touched are different.

Ok.

I was playing with the patch and tried how typenameType() would like
to force a rule so as the pstate should be always non-NULL, and got
reminded by the various callers of typenameTypeIdAndMod() that this
was a bad idea.

I'm not quite understand what you're trying to say here.
You're saying that even after this patch there will be a bunch of
places where pstate passed is NULL, but that's another issue itself
and may not be addressed within this patch?

This patch does not show how the error reports are influenced for
DefineType() and AlterType().

It does now that the `make check` failures have been fixed. However,
it doesn't appear where you would expect it to. For instance:

```
--- a/src/test/regress/expected/typed_table.out
+++ b/src/test/regress/expected/typed_table.out
 ERROR:  cannot rename column of typed table
 ALTER TABLE persons ALTER COLUMN name TYPE varchar;
 ERROR:  cannot alter column type of typed table
+LINE 1: ALTER TABLE persons ALTER COLUMN name TYPE varchar;
+                                         ^
 CREATE TABLE stuff (id int);
 ALTER TABLE persons INHERIT stuff;
 ERROR:  cannot change inheritance of typed table
```

Should we add more tests in specific places here?

This reminds as well that there is little coverage for many error
paths of DefineDomain(), with some paths actually modified in this
patch:
https://coverage.postgresql.org/src/backend/commands/typecmds.c.gcov.html
I'd suggest to do something about that, while on it, to check that the
parser issues a location on error.
--

Sure. I did add some tests to domain.sql. These tests check for almost
all parser_errposition() calls in DefineDomain.

The only thing I failed to check is "exclusion constraints not
possible for domains". Creating a domain with this type of contrians
fails earlier than DefineDomain.

Example:
```
db1=# create domain dbad as int CHECK(EXCLUDE (c) );
ERROR: column "c" does not exist
```
I'm not sure if this is possible to check.

--
Best regards,
Kirill Reshke

Attachments:

v4-0001-print-out-error-position-for-some-DDL-command.patchapplication/octet-stream; name=v4-0001-print-out-error-position-for-some-DDL-command.patchDownload
From 45d2559c33926e365e75a6c91e7e0f9c8c37b0ad Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Sat, 30 Nov 2024 14:44:38 +0800
Subject: [PATCH v4] print out error position for some DDL command.

doing this by passing the source_string to the existing ParseState
or by making a new ParseState passing source_string to it.

With this patch, the following functions will printout the error position for certain error cases.

ATExecAddOf
DefineType
ATPrepAlterColumnType
ATExecAlterColumnType
DefineDomain
AlterType

This can be particularly helpful when working with a sequence of DML commands,
such as `create schema create schema_element`.
It also makes it easier to quickly identify the relevant error area in a single DDL command.
---
 src/backend/commands/tablecmds.c              | 43 ++++++++++------
 src/backend/commands/typecmds.c               | 49 +++++++++++--------
 src/backend/parser/parse_utilcmd.c            |  2 +-
 src/backend/tcop/utility.c                    |  4 +-
 src/include/commands/typecmds.h               |  4 +-
 src/test/regress/expected/alter_table.out     | 13 +++++
 .../regress/expected/collate.icu.utf8.out     |  2 +
 src/test/regress/expected/collate.out         |  2 +
 src/test/regress/expected/domain.out          | 46 +++++++++++++++++
 src/test/regress/expected/typed_table.out     |  4 ++
 src/test/regress/sql/alter_table.sql          |  5 ++
 src/test/regress/sql/domain.sql               | 14 ++++++
 12 files changed, 148 insertions(+), 40 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6ccae4cb4a8..efa38b14702 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -593,7 +593,8 @@ static void ATPrepAlterColumnType(List **wqueue,
 								  AlterTableUtilityContext *context);
 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-										   AlterTableCmd *cmd, LOCKMODE lockmode);
+										   AlterTableCmd *cmd, LOCKMODE lockmode,
+										   AlterTableUtilityContext *context);
 static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
 											  Relation rel, AttrNumber attnum, const char *colName);
 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
@@ -639,7 +640,9 @@ static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCK
 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
 								   DependencyType deptype);
-static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
+static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename,
+								 LOCKMODE lockmode,
+								 AlterTableUtilityContext *context);
 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
 static void ATExecGenericOptions(Relation rel, List *options);
@@ -5413,7 +5416,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			break;
 		case AT_AlterColumnType:	/* ALTER COLUMN TYPE */
 			/* parse transformation was done earlier */
-			address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
+			address = ATExecAlterColumnType(tab, rel, cmd, lockmode, context);
 			break;
 		case AT_AlterColumnGenericOptions:	/* ALTER COLUMN OPTIONS */
 			address =
@@ -5537,7 +5540,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
 			break;
 		case AT_AddOf:
-			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
+			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode, context);
 			break;
 		case AT_DropOf:
 			ATExecDropOf(rel, lockmode);
@@ -13218,10 +13221,12 @@ ATPrepAlterColumnType(List **wqueue,
 	AclResult	aclresult;
 	bool		is_expr;
 
+	pstate->p_sourcetext = context->queryString;
 	if (rel->rd_rel->reloftype && !recursing)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("cannot alter column type of typed table")));
+				 errmsg("cannot alter column type of typed table"),
+				 parser_errposition(pstate, def->location)));
 
 	/* lookup the attribute so we can check inheritance status */
 	tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
@@ -13237,8 +13242,8 @@ ATPrepAlterColumnType(List **wqueue,
 	if (attnum <= 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot alter system column \"%s\"",
-						colName)));
+				 errmsg("cannot alter system column \"%s\"",colName),
+				 parser_errposition(pstate, def->location)));
 
 	/*
 	 * Cannot specify USING when altering type of a generated column, because
@@ -13271,14 +13276,14 @@ ATPrepAlterColumnType(List **wqueue,
 						colName, RelationGetRelationName(rel))));
 
 	/* Look up the target type */
-	typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
+	typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
 
 	aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error_type(aclresult, targettype);
 
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colName, targettype, targetcollid,
@@ -13537,7 +13542,8 @@ ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
  */
 static ObjectAddress
 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-					  AlterTableCmd *cmd, LOCKMODE lockmode)
+					  AlterTableCmd *cmd, LOCKMODE lockmode,
+					  AlterTableUtilityContext *context)
 {
 	char	   *colName = cmd->name;
 	ColumnDef  *def = (ColumnDef *) cmd->def;
@@ -13558,6 +13564,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	SysScanDesc scan;
 	HeapTuple	depTup;
 	ObjectAddress address;
+	ParseState      *pstate;
+
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
 
 	/*
 	 * Clear all the missing values if we're rewriting the table, since this
@@ -13596,11 +13606,11 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 						colName)));
 
 	/* Look up the target type (should not fail, since prep found it) */
-	typeTuple = typenameType(NULL, typeName, &targettypmod);
+	typeTuple = typenameType(pstate, typeName, &targettypmod);
 	tform = (Form_pg_type) GETSTRUCT(typeTuple);
 	targettype = tform->oid;
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/*
 	 * If there is a default expression for the column, get it and ensure we
@@ -16976,7 +16986,8 @@ drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
  * The address of the type is returned.
  */
 static ObjectAddress
-ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
+ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode,
+			AlterTableUtilityContext *context)
 {
 	Oid			relid = RelationGetRelid(rel);
 	Type		typetuple;
@@ -16993,9 +17004,13 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 	ObjectAddress tableobj,
 				typeobj;
 	HeapTuple	classtuple;
+	ParseState 	*pstate;
 
 	/* Validate the type. */
-	typetuple = typenameType(NULL, ofTypename, NULL);
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
+
+	typetuple = typenameType(pstate, ofTypename, NULL);
 	check_of_type(typetuple);
 	typeform = (Form_pg_type) GETSTRUCT(typetuple);
 	typeid = typeform->oid;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 971a8a1ebc5..0d0626d1ffa 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -348,7 +348,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 		Type		likeType;
 		Form_pg_type likeForm;
 
-		likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+		likeType = typenameType(pstate, defGetTypeName(likeTypeEl), NULL);
 		likeForm = (Form_pg_type) GETSTRUCT(likeType);
 		internalLength = likeForm->typlen;
 		byValue = likeForm->typbyval;
@@ -694,7 +694,7 @@ RemoveTypeById(Oid typeOid)
  *		Registers a new domain.
  */
 ObjectAddress
-DefineDomain(CreateDomainStmt *stmt)
+DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
 {
 	char	   *domainName;
 	char	   *domainArrayName;
@@ -761,7 +761,7 @@ DefineDomain(CreateDomainStmt *stmt)
 	/*
 	 * Look up the base type.
 	 */
-	typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
+	typeTup = typenameType(pstate, stmt->typeName, &basetypeMod);
 	baseType = (Form_pg_type) GETSTRUCT(typeTup);
 	basetypeoid = baseType->oid;
 
@@ -783,7 +783,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("\"%s\" is not a valid base type for a domain",
-						TypeNameToString(stmt->typeName))));
+						TypeNameToString(stmt->typeName)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	aclresult = object_aclcheck(TypeRelationId, basetypeoid, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
@@ -809,7 +810,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("collations are not supported by type %s",
-						format_type_be(basetypeoid))));
+						format_type_be(basetypeoid)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	/* passed by value */
 	byValue = baseType->typbyval;
@@ -880,17 +882,14 @@ DefineDomain(CreateDomainStmt *stmt)
 				if (saw_default)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("multiple default expressions")));
+							 errmsg("multiple default expressions"),
+							 parser_errposition(pstate, constr->location)));
 				saw_default = true;
 
 				if (constr->raw_expr)
 				{
-					ParseState *pstate;
 					Node	   *defaultExpr;
 
-					/* Create a dummy ParseState for transformExpr */
-					pstate = make_parsestate(NULL);
-
 					/*
 					 * Cook the constr->raw_expr into an expression. Note:
 					 * name is strictly for error message
@@ -943,11 +942,13 @@ DefineDomain(CreateDomainStmt *stmt)
 				if (nullDefined && !typNotNull)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							 errmsg("conflicting NULL/NOT NULL constraints"),
+							 parser_errposition(pstate, constr->location)));
 				if (constr->is_no_inherit)
 					ereport(ERROR,
-							errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							errmsg("not-null constraints for domains cannot be marked NO INHERIT"));
+							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+							 errmsg("not-null constraints for domains cannot be marked NO INHERIT"),
+							 parser_errposition(pstate, constr->location)));
 				typNotNull = true;
 				nullDefined = true;
 				break;
@@ -956,7 +957,8 @@ DefineDomain(CreateDomainStmt *stmt)
 				if (nullDefined && typNotNull)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							 errmsg("conflicting NULL/NOT NULL constraints"),
+							 parser_errposition(pstate, constr->location)));
 				typNotNull = false;
 				nullDefined = true;
 				break;
@@ -981,25 +983,29 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_UNIQUE:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("unique constraints not possible for domains")));
+						 errmsg("unique constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_PRIMARY:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("primary key constraints not possible for domains")));
+						 errmsg("primary key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_EXCLUSION:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("exclusion constraints not possible for domains")));
+						 errmsg("exclusion constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_FOREIGN:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("foreign key constraints not possible for domains")));
+						 errmsg("foreign key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_ATTR_DEFERRABLE:
@@ -1008,7 +1014,8 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_ATTR_IMMEDIATE:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("specifying constraint deferrability not supported for domains")));
+						 errmsg("specifying constraint deferrability not supported for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			default:
@@ -4315,7 +4322,7 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
  * adding new flexibility.
  */
 ObjectAddress
-AlterType(AlterTypeStmt *stmt)
+AlterType(ParseState *pstate, AlterTypeStmt *stmt)
 {
 	ObjectAddress address;
 	Relation	catalog;
@@ -4331,7 +4338,7 @@ AlterType(AlterTypeStmt *stmt)
 
 	/* Make a TypeName so we can use standard type lookup machinery */
 	typename = makeTypeNameFromNameList(stmt->typeName);
-	tup = typenameType(NULL, typename, NULL);
+	tup = typenameType(pstate, typename, NULL);
 
 	typeOid = typeTypeId(tup);
 	typForm = (Form_pg_type) GETSTRUCT(tup);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0f324ee4e31..95dad766834 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1615,7 +1615,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
 
 	Assert(ofTypename);
 
-	tuple = typenameType(NULL, ofTypename, NULL);
+	tuple = typenameType(cxt->pstate, ofTypename, NULL);
 	check_of_type(tuple);
 	ofTypeId = ((Form_pg_type) GETSTRUCT(tuple))->oid;
 	ofTypename->typeOid = ofTypeId; /* cached for later */
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f28bf371059..aa24283efff 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1712,7 +1712,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_CreateDomainStmt:
-				address = DefineDomain((CreateDomainStmt *) parsetree);
+				address = DefineDomain(pstate, (CreateDomainStmt *) parsetree);
 				break;
 
 			case T_CreateConversionStmt:
@@ -1801,7 +1801,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_AlterTypeStmt:
-				address = AlterType((AlterTypeStmt *) parsetree);
+				address = AlterType(pstate, (AlterTypeStmt *) parsetree);
 				break;
 
 			case T_CommentStmt:
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index e1b02927c4b..6e493b896c2 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -23,7 +23,7 @@
 
 extern ObjectAddress DefineType(ParseState *pstate, List *names, List *parameters);
 extern void RemoveTypeById(Oid typeOid);
-extern ObjectAddress DefineDomain(CreateDomainStmt *stmt);
+extern ObjectAddress DefineDomain(ParseState *pstate, CreateDomainStmt *stmt);
 extern ObjectAddress DefineEnum(CreateEnumStmt *stmt);
 extern ObjectAddress DefineRange(ParseState *pstate, CreateRangeStmt *stmt);
 extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
@@ -58,6 +58,6 @@ extern Oid	AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 									   bool errorOnTableType,
 									   ObjectAddresses *objsMoved);
 
-extern ObjectAddress AlterType(AlterTypeStmt *stmt);
+extern ObjectAddress AlterType(ParseState *pstate, AlterTypeStmt *stmt);
 
 #endif							/* TYPECMDS_H */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 2212c8dbb59..ebcac070630 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3413,6 +3413,19 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ERROR:  cannot alter system column "xmin"
+LINE 1: ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+                                              ^
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ERROR:  type "x" does not exist
+LINE 1: ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+                                                               ^
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+ERROR:  collations are not supported by type integer
+LINE 1: ...LE comment_test ALTER COLUMN id SET DATA TYPE int collate "C...
+                                                             ^
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
            comment           
diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out
index 9f5e57428e8..7eda33b69f8 100644
--- a/src/test/regress/expected/collate.icu.utf8.out
+++ b/src/test/regress/expected/collate.icu.utf8.out
@@ -120,6 +120,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv-x-icu";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out
index 593a6226376..bf72908fbd3 100644
--- a/src/test/regress/expected/collate.out
+++ b/src/test/regress/expected/collate.out
@@ -73,6 +73,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "P...
 CREATE DOMAIN testdomain_p AS text COLLATE "POSIX";
 CREATE DOMAIN testdomain_i AS int COLLATE "POSIX"; -- fail
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "POSIX";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_p
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 42b6559f9c8..e2f24aa43b8 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -15,6 +15,23 @@ NOTICE:  drop cascades to type dependenttypetest
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 ERROR:  type "domaindroptest" does not exist
+-- Test syntax.
+create domain d_fail  as int default 1 default 2;
+ERROR:  multiple default expressions
+LINE 1: create domain d_fail  as int default 1 default 2;
+                                               ^
+create domain d_fail int4 DEFAULT 3 + 'h';
+ERROR:  invalid input syntax for type integer: "h"
+LINE 1: create domain d_fail int4 DEFAULT 3 + 'h';
+                                              ^
+create domain d_fail int4 collate "C";
+ERROR:  collations are not supported by type integer
+LINE 1: create domain d_fail int4 collate "C";
+                             ^
+create domain d_fail as anyelement;
+ERROR:  "anyelement" is not a valid base type for a domain
+LINE 1: create domain d_fail as anyelement;
+                                ^
 -- Test domain input.
 -- Note: the point of checking both INSERT and COPY FROM is that INSERT
 -- exercises CoerceToDomain while COPY exercises domain_in.
@@ -523,6 +540,35 @@ select * from vc4table;
 
 drop table vc4table;
 drop type vc4;
+-- Test some fail cases.
+create domain dbad as int NOT NULL NULL;
+ERROR:  conflicting NULL/NOT NULL constraints
+LINE 1: create domain dbad as int NOT NULL NULL;
+                                           ^
+create domain dbad as int NOT NULL NO INHERIT;
+ERROR:  not-null constraints for domains cannot be marked NO INHERIT
+LINE 1: create domain dbad as int NOT NULL NO INHERIT;
+                                  ^
+create domain dbad as int DEFAULT 1 DEFAULT 7;
+ERROR:  multiple default expressions
+LINE 1: create domain dbad as int DEFAULT 1 DEFAULT 7;
+                                            ^
+create domain dbad as int UNIQUE;
+ERROR:  unique constraints not possible for domains
+LINE 1: create domain dbad as int UNIQUE;
+                                  ^
+create domain dbad as int PRIMARY KEY;
+ERROR:  primary key constraints not possible for domains
+LINE 1: create domain dbad as int PRIMARY KEY;
+                                  ^
+create domain dbad as int REFERENCES this_table_not_exists(i); -- we fail here in DefineDomain, so relation not exists is OK.
+ERROR:  foreign key constraints not possible for domains
+LINE 1: create domain dbad as int REFERENCES this_table_not_exists(i...
+                                  ^
+create domain dbad as int NOT NULL DEFERRABLE;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: create domain dbad as int NOT NULL DEFERRABLE;
+                                           ^
 -- You can sort of fake arrays-of-arrays by putting a domain in between
 create domain dposinta as posint[];
 create table dposintatable (f1 dposinta[]);
diff --git a/src/test/regress/expected/typed_table.out b/src/test/regress/expected/typed_table.out
index b6fbda3f217..885f085e154 100644
--- a/src/test/regress/expected/typed_table.out
+++ b/src/test/regress/expected/typed_table.out
@@ -1,5 +1,7 @@
 CREATE TABLE ttable1 OF nothing;
 ERROR:  type "nothing" does not exist
+LINE 1: CREATE TABLE ttable1 OF nothing;
+                                ^
 CREATE TYPE person_type AS (id int, name text);
 CREATE TABLE persons OF person_type;
 CREATE TABLE IF NOT EXISTS persons OF person_type;
@@ -36,6 +38,8 @@ ALTER TABLE persons RENAME COLUMN id TO num;
 ERROR:  cannot rename column of typed table
 ALTER TABLE persons ALTER COLUMN name TYPE varchar;
 ERROR:  cannot alter column type of typed table
+LINE 1: ALTER TABLE persons ALTER COLUMN name TYPE varchar;
+                                         ^
 CREATE TABLE stuff (id int);
 ALTER TABLE persons INHERIT stuff;
 ERROR:  cannot change inheritance of typed table
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 637e3dac389..f7d45932aea 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2145,6 +2145,11 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
 
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
 SELECT indexrelid::regclass::text as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass ORDER BY 1, 2;
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index ee07b03174e..704e3304339 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -16,6 +16,11 @@ drop domain domaindroptest cascade;
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 
+-- Test syntax.
+create domain d_fail  as int default 1 default 2;
+create domain d_fail int4 DEFAULT 3 + 'h';
+create domain d_fail int4 collate "C";
+create domain d_fail as anyelement;
 
 -- Test domain input.
 
@@ -273,6 +278,15 @@ select * from vc4table;
 drop table vc4table;
 drop type vc4;
 
+-- Test some fail cases.
+create domain dbad as int NOT NULL NULL;
+create domain dbad as int NOT NULL NO INHERIT;
+create domain dbad as int DEFAULT 1 DEFAULT 7;
+create domain dbad as int UNIQUE;
+create domain dbad as int PRIMARY KEY;
+create domain dbad as int REFERENCES this_table_not_exists(i); -- we fail here in DefineDomain, so relation not exists is OK.
+create domain dbad as int NOT NULL DEFERRABLE;
+
 -- You can sort of fake arrays-of-arrays by putting a domain in between
 create domain dposinta as posint[];
 create table dposintatable (f1 dposinta[]);
-- 
2.34.1

#6Kirill Reshke
reshkekirill@gmail.com
In reply to: Kirill Reshke (#5)
1 attachment(s)
Re: Pass ParseState as down to utility functions.

Looks like v4 fails on windows, PFA v5.

Sorry for the noise, I hope Cirrus CI will like this version.

--
Best regards,
Kirill Reshke

Attachments:

v5-0001-print-out-error-position-for-some-DDL-command.patchapplication/octet-stream; name=v5-0001-print-out-error-position-for-some-DDL-command.patchDownload
From e01fa721bd014926a211ffbfa315029bca680dca Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Sat, 30 Nov 2024 14:44:38 +0800
Subject: [PATCH v5] print out error position for some DDL command.

doing this by passing the source_string to the existing ParseState
or by making a new ParseState passing source_string to it.

With this patch, the following functions will printout the error position for certain error cases.

ATExecAddOf
DefineType
ATPrepAlterColumnType
ATExecAlterColumnType
DefineDomain
AlterType

This can be particularly helpful when working with a sequence of DML commands,
such as `create schema create schema_element`.
It also makes it easier to quickly identify the relevant error area in a single DDL command.
---
 src/backend/commands/tablecmds.c              | 43 ++++++++++------
 src/backend/commands/typecmds.c               | 49 +++++++++++--------
 src/backend/parser/parse_utilcmd.c            |  2 +-
 src/backend/tcop/utility.c                    |  4 +-
 src/include/commands/typecmds.h               |  4 +-
 src/test/regress/expected/alter_table.out     | 13 +++++
 .../regress/expected/collate.icu.utf8.out     |  2 +
 .../regress/expected/collate.linux.utf8.out   |  2 +
 src/test/regress/expected/collate.out         |  2 +
 .../expected/collate.windows.win1252.out      |  2 +
 src/test/regress/expected/domain.out          | 46 +++++++++++++++++
 src/test/regress/expected/typed_table.out     |  4 ++
 src/test/regress/sql/alter_table.sql          |  5 ++
 src/test/regress/sql/domain.sql               | 14 ++++++
 14 files changed, 152 insertions(+), 40 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6ccae4cb4a8..efa38b14702 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -593,7 +593,8 @@ static void ATPrepAlterColumnType(List **wqueue,
 								  AlterTableUtilityContext *context);
 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-										   AlterTableCmd *cmd, LOCKMODE lockmode);
+										   AlterTableCmd *cmd, LOCKMODE lockmode,
+										   AlterTableUtilityContext *context);
 static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
 											  Relation rel, AttrNumber attnum, const char *colName);
 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
@@ -639,7 +640,9 @@ static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCK
 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
 								   DependencyType deptype);
-static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
+static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename,
+								 LOCKMODE lockmode,
+								 AlterTableUtilityContext *context);
 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
 static void ATExecGenericOptions(Relation rel, List *options);
@@ -5413,7 +5416,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			break;
 		case AT_AlterColumnType:	/* ALTER COLUMN TYPE */
 			/* parse transformation was done earlier */
-			address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
+			address = ATExecAlterColumnType(tab, rel, cmd, lockmode, context);
 			break;
 		case AT_AlterColumnGenericOptions:	/* ALTER COLUMN OPTIONS */
 			address =
@@ -5537,7 +5540,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
 			break;
 		case AT_AddOf:
-			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
+			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode, context);
 			break;
 		case AT_DropOf:
 			ATExecDropOf(rel, lockmode);
@@ -13218,10 +13221,12 @@ ATPrepAlterColumnType(List **wqueue,
 	AclResult	aclresult;
 	bool		is_expr;
 
+	pstate->p_sourcetext = context->queryString;
 	if (rel->rd_rel->reloftype && !recursing)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("cannot alter column type of typed table")));
+				 errmsg("cannot alter column type of typed table"),
+				 parser_errposition(pstate, def->location)));
 
 	/* lookup the attribute so we can check inheritance status */
 	tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
@@ -13237,8 +13242,8 @@ ATPrepAlterColumnType(List **wqueue,
 	if (attnum <= 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot alter system column \"%s\"",
-						colName)));
+				 errmsg("cannot alter system column \"%s\"",colName),
+				 parser_errposition(pstate, def->location)));
 
 	/*
 	 * Cannot specify USING when altering type of a generated column, because
@@ -13271,14 +13276,14 @@ ATPrepAlterColumnType(List **wqueue,
 						colName, RelationGetRelationName(rel))));
 
 	/* Look up the target type */
-	typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
+	typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
 
 	aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error_type(aclresult, targettype);
 
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colName, targettype, targetcollid,
@@ -13537,7 +13542,8 @@ ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
  */
 static ObjectAddress
 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-					  AlterTableCmd *cmd, LOCKMODE lockmode)
+					  AlterTableCmd *cmd, LOCKMODE lockmode,
+					  AlterTableUtilityContext *context)
 {
 	char	   *colName = cmd->name;
 	ColumnDef  *def = (ColumnDef *) cmd->def;
@@ -13558,6 +13564,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	SysScanDesc scan;
 	HeapTuple	depTup;
 	ObjectAddress address;
+	ParseState      *pstate;
+
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
 
 	/*
 	 * Clear all the missing values if we're rewriting the table, since this
@@ -13596,11 +13606,11 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 						colName)));
 
 	/* Look up the target type (should not fail, since prep found it) */
-	typeTuple = typenameType(NULL, typeName, &targettypmod);
+	typeTuple = typenameType(pstate, typeName, &targettypmod);
 	tform = (Form_pg_type) GETSTRUCT(typeTuple);
 	targettype = tform->oid;
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/*
 	 * If there is a default expression for the column, get it and ensure we
@@ -16976,7 +16986,8 @@ drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
  * The address of the type is returned.
  */
 static ObjectAddress
-ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
+ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode,
+			AlterTableUtilityContext *context)
 {
 	Oid			relid = RelationGetRelid(rel);
 	Type		typetuple;
@@ -16993,9 +17004,13 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 	ObjectAddress tableobj,
 				typeobj;
 	HeapTuple	classtuple;
+	ParseState 	*pstate;
 
 	/* Validate the type. */
-	typetuple = typenameType(NULL, ofTypename, NULL);
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
+
+	typetuple = typenameType(pstate, ofTypename, NULL);
 	check_of_type(typetuple);
 	typeform = (Form_pg_type) GETSTRUCT(typetuple);
 	typeid = typeform->oid;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 971a8a1ebc5..0d0626d1ffa 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -348,7 +348,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 		Type		likeType;
 		Form_pg_type likeForm;
 
-		likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+		likeType = typenameType(pstate, defGetTypeName(likeTypeEl), NULL);
 		likeForm = (Form_pg_type) GETSTRUCT(likeType);
 		internalLength = likeForm->typlen;
 		byValue = likeForm->typbyval;
@@ -694,7 +694,7 @@ RemoveTypeById(Oid typeOid)
  *		Registers a new domain.
  */
 ObjectAddress
-DefineDomain(CreateDomainStmt *stmt)
+DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
 {
 	char	   *domainName;
 	char	   *domainArrayName;
@@ -761,7 +761,7 @@ DefineDomain(CreateDomainStmt *stmt)
 	/*
 	 * Look up the base type.
 	 */
-	typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
+	typeTup = typenameType(pstate, stmt->typeName, &basetypeMod);
 	baseType = (Form_pg_type) GETSTRUCT(typeTup);
 	basetypeoid = baseType->oid;
 
@@ -783,7 +783,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("\"%s\" is not a valid base type for a domain",
-						TypeNameToString(stmt->typeName))));
+						TypeNameToString(stmt->typeName)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	aclresult = object_aclcheck(TypeRelationId, basetypeoid, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
@@ -809,7 +810,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("collations are not supported by type %s",
-						format_type_be(basetypeoid))));
+						format_type_be(basetypeoid)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	/* passed by value */
 	byValue = baseType->typbyval;
@@ -880,17 +882,14 @@ DefineDomain(CreateDomainStmt *stmt)
 				if (saw_default)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("multiple default expressions")));
+							 errmsg("multiple default expressions"),
+							 parser_errposition(pstate, constr->location)));
 				saw_default = true;
 
 				if (constr->raw_expr)
 				{
-					ParseState *pstate;
 					Node	   *defaultExpr;
 
-					/* Create a dummy ParseState for transformExpr */
-					pstate = make_parsestate(NULL);
-
 					/*
 					 * Cook the constr->raw_expr into an expression. Note:
 					 * name is strictly for error message
@@ -943,11 +942,13 @@ DefineDomain(CreateDomainStmt *stmt)
 				if (nullDefined && !typNotNull)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							 errmsg("conflicting NULL/NOT NULL constraints"),
+							 parser_errposition(pstate, constr->location)));
 				if (constr->is_no_inherit)
 					ereport(ERROR,
-							errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							errmsg("not-null constraints for domains cannot be marked NO INHERIT"));
+							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+							 errmsg("not-null constraints for domains cannot be marked NO INHERIT"),
+							 parser_errposition(pstate, constr->location)));
 				typNotNull = true;
 				nullDefined = true;
 				break;
@@ -956,7 +957,8 @@ DefineDomain(CreateDomainStmt *stmt)
 				if (nullDefined && typNotNull)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							 errmsg("conflicting NULL/NOT NULL constraints"),
+							 parser_errposition(pstate, constr->location)));
 				typNotNull = false;
 				nullDefined = true;
 				break;
@@ -981,25 +983,29 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_UNIQUE:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("unique constraints not possible for domains")));
+						 errmsg("unique constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_PRIMARY:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("primary key constraints not possible for domains")));
+						 errmsg("primary key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_EXCLUSION:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("exclusion constraints not possible for domains")));
+						 errmsg("exclusion constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_FOREIGN:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("foreign key constraints not possible for domains")));
+						 errmsg("foreign key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_ATTR_DEFERRABLE:
@@ -1008,7 +1014,8 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_ATTR_IMMEDIATE:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("specifying constraint deferrability not supported for domains")));
+						 errmsg("specifying constraint deferrability not supported for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			default:
@@ -4315,7 +4322,7 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
  * adding new flexibility.
  */
 ObjectAddress
-AlterType(AlterTypeStmt *stmt)
+AlterType(ParseState *pstate, AlterTypeStmt *stmt)
 {
 	ObjectAddress address;
 	Relation	catalog;
@@ -4331,7 +4338,7 @@ AlterType(AlterTypeStmt *stmt)
 
 	/* Make a TypeName so we can use standard type lookup machinery */
 	typename = makeTypeNameFromNameList(stmt->typeName);
-	tup = typenameType(NULL, typename, NULL);
+	tup = typenameType(pstate, typename, NULL);
 
 	typeOid = typeTypeId(tup);
 	typForm = (Form_pg_type) GETSTRUCT(tup);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0f324ee4e31..95dad766834 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1615,7 +1615,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
 
 	Assert(ofTypename);
 
-	tuple = typenameType(NULL, ofTypename, NULL);
+	tuple = typenameType(cxt->pstate, ofTypename, NULL);
 	check_of_type(tuple);
 	ofTypeId = ((Form_pg_type) GETSTRUCT(tuple))->oid;
 	ofTypename->typeOid = ofTypeId; /* cached for later */
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f28bf371059..aa24283efff 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1712,7 +1712,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_CreateDomainStmt:
-				address = DefineDomain((CreateDomainStmt *) parsetree);
+				address = DefineDomain(pstate, (CreateDomainStmt *) parsetree);
 				break;
 
 			case T_CreateConversionStmt:
@@ -1801,7 +1801,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_AlterTypeStmt:
-				address = AlterType((AlterTypeStmt *) parsetree);
+				address = AlterType(pstate, (AlterTypeStmt *) parsetree);
 				break;
 
 			case T_CommentStmt:
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index e1b02927c4b..6e493b896c2 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -23,7 +23,7 @@
 
 extern ObjectAddress DefineType(ParseState *pstate, List *names, List *parameters);
 extern void RemoveTypeById(Oid typeOid);
-extern ObjectAddress DefineDomain(CreateDomainStmt *stmt);
+extern ObjectAddress DefineDomain(ParseState *pstate, CreateDomainStmt *stmt);
 extern ObjectAddress DefineEnum(CreateEnumStmt *stmt);
 extern ObjectAddress DefineRange(ParseState *pstate, CreateRangeStmt *stmt);
 extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
@@ -58,6 +58,6 @@ extern Oid	AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 									   bool errorOnTableType,
 									   ObjectAddresses *objsMoved);
 
-extern ObjectAddress AlterType(AlterTypeStmt *stmt);
+extern ObjectAddress AlterType(ParseState *pstate, AlterTypeStmt *stmt);
 
 #endif							/* TYPECMDS_H */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 2212c8dbb59..ebcac070630 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3413,6 +3413,19 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ERROR:  cannot alter system column "xmin"
+LINE 1: ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+                                              ^
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ERROR:  type "x" does not exist
+LINE 1: ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+                                                               ^
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+ERROR:  collations are not supported by type integer
+LINE 1: ...LE comment_test ALTER COLUMN id SET DATA TYPE int collate "C...
+                                                             ^
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
            comment           
diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out
index 9f5e57428e8..7eda33b69f8 100644
--- a/src/test/regress/expected/collate.icu.utf8.out
+++ b/src/test/regress/expected/collate.icu.utf8.out
@@ -120,6 +120,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv-x-icu";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out
index 01664f7c1b5..fbaab7cdf83 100644
--- a/src/test/regress/expected/collate.linux.utf8.out
+++ b/src/test/regress/expected/collate.linux.utf8.out
@@ -122,6 +122,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out
index 593a6226376..bf72908fbd3 100644
--- a/src/test/regress/expected/collate.out
+++ b/src/test/regress/expected/collate.out
@@ -73,6 +73,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "P...
 CREATE DOMAIN testdomain_p AS text COLLATE "POSIX";
 CREATE DOMAIN testdomain_i AS int COLLATE "POSIX"; -- fail
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "POSIX";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_p
diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out
index 31f794988b3..4644f56b31d 100644
--- a/src/test/regress/expected/collate.windows.win1252.out
+++ b/src/test/regress/expected/collate.windows.win1252.out
@@ -124,6 +124,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 42b6559f9c8..e2f24aa43b8 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -15,6 +15,23 @@ NOTICE:  drop cascades to type dependenttypetest
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 ERROR:  type "domaindroptest" does not exist
+-- Test syntax.
+create domain d_fail  as int default 1 default 2;
+ERROR:  multiple default expressions
+LINE 1: create domain d_fail  as int default 1 default 2;
+                                               ^
+create domain d_fail int4 DEFAULT 3 + 'h';
+ERROR:  invalid input syntax for type integer: "h"
+LINE 1: create domain d_fail int4 DEFAULT 3 + 'h';
+                                              ^
+create domain d_fail int4 collate "C";
+ERROR:  collations are not supported by type integer
+LINE 1: create domain d_fail int4 collate "C";
+                             ^
+create domain d_fail as anyelement;
+ERROR:  "anyelement" is not a valid base type for a domain
+LINE 1: create domain d_fail as anyelement;
+                                ^
 -- Test domain input.
 -- Note: the point of checking both INSERT and COPY FROM is that INSERT
 -- exercises CoerceToDomain while COPY exercises domain_in.
@@ -523,6 +540,35 @@ select * from vc4table;
 
 drop table vc4table;
 drop type vc4;
+-- Test some fail cases.
+create domain dbad as int NOT NULL NULL;
+ERROR:  conflicting NULL/NOT NULL constraints
+LINE 1: create domain dbad as int NOT NULL NULL;
+                                           ^
+create domain dbad as int NOT NULL NO INHERIT;
+ERROR:  not-null constraints for domains cannot be marked NO INHERIT
+LINE 1: create domain dbad as int NOT NULL NO INHERIT;
+                                  ^
+create domain dbad as int DEFAULT 1 DEFAULT 7;
+ERROR:  multiple default expressions
+LINE 1: create domain dbad as int DEFAULT 1 DEFAULT 7;
+                                            ^
+create domain dbad as int UNIQUE;
+ERROR:  unique constraints not possible for domains
+LINE 1: create domain dbad as int UNIQUE;
+                                  ^
+create domain dbad as int PRIMARY KEY;
+ERROR:  primary key constraints not possible for domains
+LINE 1: create domain dbad as int PRIMARY KEY;
+                                  ^
+create domain dbad as int REFERENCES this_table_not_exists(i); -- we fail here in DefineDomain, so relation not exists is OK.
+ERROR:  foreign key constraints not possible for domains
+LINE 1: create domain dbad as int REFERENCES this_table_not_exists(i...
+                                  ^
+create domain dbad as int NOT NULL DEFERRABLE;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: create domain dbad as int NOT NULL DEFERRABLE;
+                                           ^
 -- You can sort of fake arrays-of-arrays by putting a domain in between
 create domain dposinta as posint[];
 create table dposintatable (f1 dposinta[]);
diff --git a/src/test/regress/expected/typed_table.out b/src/test/regress/expected/typed_table.out
index b6fbda3f217..885f085e154 100644
--- a/src/test/regress/expected/typed_table.out
+++ b/src/test/regress/expected/typed_table.out
@@ -1,5 +1,7 @@
 CREATE TABLE ttable1 OF nothing;
 ERROR:  type "nothing" does not exist
+LINE 1: CREATE TABLE ttable1 OF nothing;
+                                ^
 CREATE TYPE person_type AS (id int, name text);
 CREATE TABLE persons OF person_type;
 CREATE TABLE IF NOT EXISTS persons OF person_type;
@@ -36,6 +38,8 @@ ALTER TABLE persons RENAME COLUMN id TO num;
 ERROR:  cannot rename column of typed table
 ALTER TABLE persons ALTER COLUMN name TYPE varchar;
 ERROR:  cannot alter column type of typed table
+LINE 1: ALTER TABLE persons ALTER COLUMN name TYPE varchar;
+                                         ^
 CREATE TABLE stuff (id int);
 ALTER TABLE persons INHERIT stuff;
 ERROR:  cannot change inheritance of typed table
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 637e3dac389..f7d45932aea 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2145,6 +2145,11 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
 
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
 SELECT indexrelid::regclass::text as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass ORDER BY 1, 2;
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index ee07b03174e..704e3304339 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -16,6 +16,11 @@ drop domain domaindroptest cascade;
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 
+-- Test syntax.
+create domain d_fail  as int default 1 default 2;
+create domain d_fail int4 DEFAULT 3 + 'h';
+create domain d_fail int4 collate "C";
+create domain d_fail as anyelement;
 
 -- Test domain input.
 
@@ -273,6 +278,15 @@ select * from vc4table;
 drop table vc4table;
 drop type vc4;
 
+-- Test some fail cases.
+create domain dbad as int NOT NULL NULL;
+create domain dbad as int NOT NULL NO INHERIT;
+create domain dbad as int DEFAULT 1 DEFAULT 7;
+create domain dbad as int UNIQUE;
+create domain dbad as int PRIMARY KEY;
+create domain dbad as int REFERENCES this_table_not_exists(i); -- we fail here in DefineDomain, so relation not exists is OK.
+create domain dbad as int NOT NULL DEFERRABLE;
+
 -- You can sort of fake arrays-of-arrays by putting a domain in between
 create domain dposinta as posint[];
 create table dposintatable (f1 dposinta[]);
-- 
2.34.1

#7jian he
jian.universality@gmail.com
In reply to: Kirill Reshke (#6)
1 attachment(s)
Re: Pass ParseState as down to utility functions.

hi.

extensive test for
ATExecAddOf
DefineType
ATPrepAlterColumnType
ATExecAlterColumnType
DefineDomain
AlterType
transformAlterTableStmt

only AlterType, ATExecAlterColumnType function code change no tests.
AlterType doesn't have location info, can not print it out.
ATExecAlterColumnType is unreachable, because ATPrepAlterColumnType
catched most of the error.

especially extensive tests for DefineDomain.
AlterDomainAddConstraint related error case, i created another thread
(refactor AlterDomainAddConstraint (alter domain add constraint))

Attachments:

v6-0001-print-out-error-position-for-some-DDL-command.patchtext/x-patch; charset=US-ASCII; name=v6-0001-print-out-error-position-for-some-DDL-command.patchDownload
From 6bf657c3b62b7460b317c42ce2f4fa0988acf1a0 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Fri, 6 Dec 2024 16:37:18 +0800
Subject: [PATCH v6 1/1] print out error position for some DDL command

doing this by passing the source_string to the existing ParseState
or by making a new ParseState passing source_string to it.

With this patch, the following functions will printout the error position for certain error cases.

ATExecAddOf
DefineType
ATPrepAlterColumnType
ATExecAlterColumnType
DefineDomain
AlterType
transformAlterTableStmt

ATExecAlterColumnType code change maybe not necessary, since
ATPrepAlterColumnType will catach most of the error.  AlterType function changes
no effect since struct AlterTypeStmt don't have location info.

ATExecAlterColumnType, AlterType because of the above mentioned reason, don't
have regress test.  all other have tests.

This can be particularly helpful when working with a sequence of DML commands,
such as `create schema create schema_element`.
It also makes it easier to quickly identify the relevant error area in a single DDL command

extensive regerss tests (failed case) for CREATE DOMAIN added.

discussion: https://postgr.es/m/CALdSSPhqfvKbDwqJaY=yEePi_aq61GmMpW88i6ZH7CMG_2Z4Cg@mail.gmail.com
---
 src/backend/commands/tablecmds.c              | 43 +++++++----
 src/backend/commands/typecmds.c               | 56 ++++++++------
 src/backend/parser/parse_utilcmd.c            |  8 +-
 src/backend/tcop/utility.c                    |  4 +-
 src/include/commands/typecmds.h               |  4 +-
 src/test/regress/expected/alter_table.out     | 21 ++++++
 .../regress/expected/collate.icu.utf8.out     |  2 +
 .../regress/expected/collate.linux.utf8.out   |  2 +
 src/test/regress/expected/collate.out         |  2 +
 .../expected/collate.windows.win1252.out      |  2 +
 src/test/regress/expected/domain.out          | 74 +++++++++++++++++++
 src/test/regress/expected/float8.out          |  5 ++
 src/test/regress/expected/identity.out        |  4 +
 src/test/regress/expected/typed_table.out     |  4 +
 src/test/regress/sql/alter_table.sql          |  7 ++
 src/test/regress/sql/domain.sql               | 21 ++++++
 src/test/regress/sql/float8.sql               |  2 +
 src/test/regress/sql/identity.sql             |  1 +
 18 files changed, 218 insertions(+), 44 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6ccae4cb4a..efa38b1470 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -593,7 +593,8 @@ static void ATPrepAlterColumnType(List **wqueue,
 								  AlterTableUtilityContext *context);
 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-										   AlterTableCmd *cmd, LOCKMODE lockmode);
+										   AlterTableCmd *cmd, LOCKMODE lockmode,
+										   AlterTableUtilityContext *context);
 static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
 											  Relation rel, AttrNumber attnum, const char *colName);
 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
@@ -639,7 +640,9 @@ static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCK
 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
 								   DependencyType deptype);
-static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
+static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename,
+								 LOCKMODE lockmode,
+								 AlterTableUtilityContext *context);
 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
 static void ATExecGenericOptions(Relation rel, List *options);
@@ -5413,7 +5416,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			break;
 		case AT_AlterColumnType:	/* ALTER COLUMN TYPE */
 			/* parse transformation was done earlier */
-			address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
+			address = ATExecAlterColumnType(tab, rel, cmd, lockmode, context);
 			break;
 		case AT_AlterColumnGenericOptions:	/* ALTER COLUMN OPTIONS */
 			address =
@@ -5537,7 +5540,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
 			break;
 		case AT_AddOf:
-			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
+			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode, context);
 			break;
 		case AT_DropOf:
 			ATExecDropOf(rel, lockmode);
@@ -13218,10 +13221,12 @@ ATPrepAlterColumnType(List **wqueue,
 	AclResult	aclresult;
 	bool		is_expr;
 
+	pstate->p_sourcetext = context->queryString;
 	if (rel->rd_rel->reloftype && !recursing)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("cannot alter column type of typed table")));
+				 errmsg("cannot alter column type of typed table"),
+				 parser_errposition(pstate, def->location)));
 
 	/* lookup the attribute so we can check inheritance status */
 	tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
@@ -13237,8 +13242,8 @@ ATPrepAlterColumnType(List **wqueue,
 	if (attnum <= 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot alter system column \"%s\"",
-						colName)));
+				 errmsg("cannot alter system column \"%s\"",colName),
+				 parser_errposition(pstate, def->location)));
 
 	/*
 	 * Cannot specify USING when altering type of a generated column, because
@@ -13271,14 +13276,14 @@ ATPrepAlterColumnType(List **wqueue,
 						colName, RelationGetRelationName(rel))));
 
 	/* Look up the target type */
-	typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
+	typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
 
 	aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error_type(aclresult, targettype);
 
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colName, targettype, targetcollid,
@@ -13537,7 +13542,8 @@ ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
  */
 static ObjectAddress
 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-					  AlterTableCmd *cmd, LOCKMODE lockmode)
+					  AlterTableCmd *cmd, LOCKMODE lockmode,
+					  AlterTableUtilityContext *context)
 {
 	char	   *colName = cmd->name;
 	ColumnDef  *def = (ColumnDef *) cmd->def;
@@ -13558,6 +13564,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	SysScanDesc scan;
 	HeapTuple	depTup;
 	ObjectAddress address;
+	ParseState      *pstate;
+
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
 
 	/*
 	 * Clear all the missing values if we're rewriting the table, since this
@@ -13596,11 +13606,11 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 						colName)));
 
 	/* Look up the target type (should not fail, since prep found it) */
-	typeTuple = typenameType(NULL, typeName, &targettypmod);
+	typeTuple = typenameType(pstate, typeName, &targettypmod);
 	tform = (Form_pg_type) GETSTRUCT(typeTuple);
 	targettype = tform->oid;
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/*
 	 * If there is a default expression for the column, get it and ensure we
@@ -16976,7 +16986,8 @@ drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
  * The address of the type is returned.
  */
 static ObjectAddress
-ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
+ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode,
+			AlterTableUtilityContext *context)
 {
 	Oid			relid = RelationGetRelid(rel);
 	Type		typetuple;
@@ -16993,9 +17004,13 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 	ObjectAddress tableobj,
 				typeobj;
 	HeapTuple	classtuple;
+	ParseState 	*pstate;
 
 	/* Validate the type. */
-	typetuple = typenameType(NULL, ofTypename, NULL);
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
+
+	typetuple = typenameType(pstate, ofTypename, NULL);
 	check_of_type(typetuple);
 	typeform = (Form_pg_type) GETSTRUCT(typetuple);
 	typeid = typeform->oid;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index da591c0922..7d9d911d48 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -348,7 +348,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 		Type		likeType;
 		Form_pg_type likeForm;
 
-		likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+		likeType = typenameType(pstate, defGetTypeName(likeTypeEl), NULL);
 		likeForm = (Form_pg_type) GETSTRUCT(likeType);
 		internalLength = likeForm->typlen;
 		byValue = likeForm->typbyval;
@@ -694,7 +694,7 @@ RemoveTypeById(Oid typeOid)
  *		Registers a new domain.
  */
 ObjectAddress
-DefineDomain(CreateDomainStmt *stmt)
+DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
 {
 	char	   *domainName;
 	char	   *domainArrayName;
@@ -761,7 +761,7 @@ DefineDomain(CreateDomainStmt *stmt)
 	/*
 	 * Look up the base type.
 	 */
-	typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
+	typeTup = typenameType(pstate, stmt->typeName, &basetypeMod);
 	baseType = (Form_pg_type) GETSTRUCT(typeTup);
 	basetypeoid = baseType->oid;
 
@@ -783,7 +783,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("\"%s\" is not a valid base type for a domain",
-						TypeNameToString(stmt->typeName))));
+						TypeNameToString(stmt->typeName)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	aclresult = object_aclcheck(TypeRelationId, basetypeoid, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
@@ -809,7 +810,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("collations are not supported by type %s",
-						format_type_be(basetypeoid))));
+						format_type_be(basetypeoid)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	/* passed by value */
 	byValue = baseType->typbyval;
@@ -880,17 +882,14 @@ DefineDomain(CreateDomainStmt *stmt)
 				if (saw_default)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("multiple default expressions")));
+							 errmsg("multiple default expressions"),
+							 parser_errposition(pstate, constr->location)));
 				saw_default = true;
 
 				if (constr->raw_expr)
 				{
-					ParseState *pstate;
 					Node	   *defaultExpr;
 
-					/* Create a dummy ParseState for transformExpr */
-					pstate = make_parsestate(NULL);
-
 					/*
 					 * Cook the constr->raw_expr into an expression. Note:
 					 * name is strictly for error message
@@ -943,11 +942,13 @@ DefineDomain(CreateDomainStmt *stmt)
 				if (nullDefined && !typNotNull)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							 errmsg("conflicting NULL/NOT NULL constraints"),
+							 parser_errposition(pstate, constr->location)));
 				if (constr->is_no_inherit)
 					ereport(ERROR,
-							errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							errmsg("not-null constraints for domains cannot be marked NO INHERIT"));
+							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+							 errmsg("not-null constraints for domains cannot be marked NO INHERIT"),
+							 parser_errposition(pstate, constr->location)));
 				typNotNull = true;
 				nullDefined = true;
 				break;
@@ -956,7 +957,8 @@ DefineDomain(CreateDomainStmt *stmt)
 				if (nullDefined && typNotNull)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							 errmsg("conflicting NULL/NOT NULL constraints"),
+							 parser_errposition(pstate, constr->location)));
 				typNotNull = false;
 				nullDefined = true;
 				break;
@@ -972,7 +974,9 @@ DefineDomain(CreateDomainStmt *stmt)
 				if (constr->is_no_inherit)
 					ereport(ERROR,
 							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							 errmsg("check constraints for domains cannot be marked NO INHERIT")));
+							 errmsg("check constraints for domains cannot be marked NO INHERIT"),
+							 parser_errposition(pstate, constr->location)));
+
 				break;
 
 				/*
@@ -981,25 +985,29 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_UNIQUE:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("unique constraints not possible for domains")));
+						 errmsg("unique constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_PRIMARY:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("primary key constraints not possible for domains")));
+						 errmsg("primary key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_EXCLUSION:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("exclusion constraints not possible for domains")));
+						 errmsg("exclusion constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_FOREIGN:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("foreign key constraints not possible for domains")));
+						 errmsg("foreign key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_ATTR_DEFERRABLE:
@@ -1008,14 +1016,16 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_ATTR_IMMEDIATE:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("specifying constraint deferrability not supported for domains")));
+						 errmsg("specifying constraint deferrability not supported for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_GENERATED:
 			case CONSTR_IDENTITY:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("specifying GENERATED not supported for domains")));
+						 errmsg("specifying GENERATED not supported for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 				/* no default, to let compiler warn about missing case */
@@ -4319,7 +4329,7 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
  * adding new flexibility.
  */
 ObjectAddress
-AlterType(AlterTypeStmt *stmt)
+AlterType(ParseState *pstate, AlterTypeStmt *stmt)
 {
 	ObjectAddress address;
 	Relation	catalog;
@@ -4335,7 +4345,7 @@ AlterType(AlterTypeStmt *stmt)
 
 	/* Make a TypeName so we can use standard type lookup machinery */
 	typename = makeTypeNameFromNameList(stmt->typeName);
-	tup = typenameType(NULL, typename, NULL);
+	tup = typenameType(pstate, typename, NULL);
 
 	typeOid = typeTypeId(tup);
 	typForm = (Form_pg_type) GETSTRUCT(tup);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0f324ee4e3..67a41ac9f1 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1615,7 +1615,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
 
 	Assert(ofTypename);
 
-	tuple = typenameType(NULL, ofTypename, NULL);
+	tuple = typenameType(cxt->pstate, ofTypename, NULL);
 	check_of_type(tuple);
 	ofTypeId = ((Form_pg_type) GETSTRUCT(tuple))->oid;
 	ofTypename->typeOid = ofTypeId; /* cached for later */
@@ -3627,7 +3627,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 							ereport(ERROR,
 									(errcode(ERRCODE_UNDEFINED_COLUMN),
 									 errmsg("column \"%s\" of relation \"%s\" does not exist",
-											cmd->name, RelationGetRelationName(rel))));
+											cmd->name, RelationGetRelationName(rel)),
+									 parser_errposition(pstate, def->location)));
 
 						if (attnum > 0 &&
 							TupleDescAttr(tupdesc, attnum - 1)->attidentity)
@@ -3667,7 +3668,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 						ereport(ERROR,
 								(errcode(ERRCODE_UNDEFINED_COLUMN),
 								 errmsg("column \"%s\" of relation \"%s\" does not exist",
-										cmd->name, RelationGetRelationName(rel))));
+										cmd->name, RelationGetRelationName(rel)),
+								 parser_errposition(pstate, def->location)));
 
 					generateSerialExtraStmts(&cxt, newdef,
 											 get_atttype(relid, attnum),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f28bf37105..aa24283eff 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1712,7 +1712,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_CreateDomainStmt:
-				address = DefineDomain((CreateDomainStmt *) parsetree);
+				address = DefineDomain(pstate, (CreateDomainStmt *) parsetree);
 				break;
 
 			case T_CreateConversionStmt:
@@ -1801,7 +1801,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_AlterTypeStmt:
-				address = AlterType((AlterTypeStmt *) parsetree);
+				address = AlterType(pstate, (AlterTypeStmt *) parsetree);
 				break;
 
 			case T_CommentStmt:
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index e1b02927c4..6e493b896c 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -23,7 +23,7 @@
 
 extern ObjectAddress DefineType(ParseState *pstate, List *names, List *parameters);
 extern void RemoveTypeById(Oid typeOid);
-extern ObjectAddress DefineDomain(CreateDomainStmt *stmt);
+extern ObjectAddress DefineDomain(ParseState *pstate, CreateDomainStmt *stmt);
 extern ObjectAddress DefineEnum(CreateEnumStmt *stmt);
 extern ObjectAddress DefineRange(ParseState *pstate, CreateRangeStmt *stmt);
 extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
@@ -58,6 +58,6 @@ extern Oid	AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 									   bool errorOnTableType,
 									   ObjectAddresses *objsMoved);
 
-extern ObjectAddress AlterType(AlterTypeStmt *stmt);
+extern ObjectAddress AlterType(ParseState *pstate, AlterTypeStmt *stmt);
 
 #endif							/* TYPECMDS_H */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 2212c8dbb5..ff64a23a5f 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3112,6 +3112,10 @@ ERROR:  cannot alter type "test_type1" because column "test_tbl1_idx.row" uses i
 DROP TABLE test_tbl1;
 DROP TYPE test_type1;
 CREATE TYPE test_type2 AS (a int, b text);
+CREATE TABLE test_tbl2 OF xx;
+ERROR:  type "xx" does not exist
+LINE 1: CREATE TABLE test_tbl2 OF xx;
+                                  ^
 CREATE TABLE test_tbl2 OF test_type2;
 CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2);
 \d test_type2
@@ -3263,6 +3267,10 @@ CREATE TABLE tt5 (x int, y numeric(8,2), z int);	-- too few columns
 CREATE TABLE tt6 () INHERITS (tt0);					-- can't have a parent
 CREATE TABLE tt7 (x int, q text, y numeric(8,2));
 ALTER TABLE tt7 DROP q;								-- OK
+ALTER TABLE tt0 OF tt_t_noexist;
+ERROR:  type "tt_t_noexist" does not exist
+LINE 1: ALTER TABLE tt0 OF tt_t_noexist;
+                           ^
 ALTER TABLE tt0 OF tt_t0;
 ALTER TABLE tt1 OF tt_t0;
 ERROR:  table "tt1" has different type for column "y"
@@ -3413,6 +3421,19 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ERROR:  cannot alter system column "xmin"
+LINE 1: ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+                                              ^
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ERROR:  type "x" does not exist
+LINE 1: ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+                                                               ^
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+ERROR:  collations are not supported by type integer
+LINE 1: ...LE comment_test ALTER COLUMN id SET DATA TYPE int collate "C...
+                                                             ^
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
            comment           
diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out
index 6cbadafcfb..d4f327636f 100644
--- a/src/test/regress/expected/collate.icu.utf8.out
+++ b/src/test/regress/expected/collate.icu.utf8.out
@@ -120,6 +120,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv-x-icu";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out
index 01664f7c1b..fbaab7cdf8 100644
--- a/src/test/regress/expected/collate.linux.utf8.out
+++ b/src/test/regress/expected/collate.linux.utf8.out
@@ -122,6 +122,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out
index 593a622637..bf72908fbd 100644
--- a/src/test/regress/expected/collate.out
+++ b/src/test/regress/expected/collate.out
@@ -73,6 +73,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "P...
 CREATE DOMAIN testdomain_p AS text COLLATE "POSIX";
 CREATE DOMAIN testdomain_i AS int COLLATE "POSIX"; -- fail
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "POSIX";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_p
diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out
index 31f794988b..4644f56b31 100644
--- a/src/test/regress/expected/collate.windows.win1252.out
+++ b/src/test/regress/expected/collate.windows.win1252.out
@@ -124,6 +124,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 42b6559f9c..222bb54c75 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -15,6 +15,80 @@ NOTICE:  drop cascades to type dependenttypetest
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 ERROR:  type "domaindroptest" does not exist
+-- Test syntax.
+-- create domain error case.
+create domain d_fail as x;
+ERROR:  type "x" does not exist
+LINE 1: create domain d_fail as x;
+                                ^
+create domain d_fail as int constraint cc REFERENCES this_table_not_exists(i);
+ERROR:  foreign key constraints not possible for domains
+LINE 1: create domain d_fail as int constraint cc REFERENCES this_ta...
+                                    ^
+create domain d_fail as int4 not null no inherit;
+ERROR:  not-null constraints for domains cannot be marked NO INHERIT
+LINE 1: create domain d_fail as int4 not null no inherit;
+                                     ^
+create domain d_fail as int4 not null null;
+ERROR:  conflicting NULL/NOT NULL constraints
+LINE 1: create domain d_fail as int4 not null null;
+                                              ^
+create domain d_fail as int4 not null default 3 default 3;
+ERROR:  multiple default expressions
+LINE 1: create domain d_fail as int4 not null default 3 default 3;
+                                                        ^
+create domain d_fail int4 DEFAULT 3 + 'h';
+ERROR:  invalid input syntax for type integer: "h"
+LINE 1: create domain d_fail int4 DEFAULT 3 + 'h';
+                                              ^
+create domain d_fail int4 collate "C";
+ERROR:  collations are not supported by type integer
+LINE 1: create domain d_fail int4 collate "C";
+                             ^
+create domain d_fail as anyelement;
+ERROR:  "anyelement" is not a valid base type for a domain
+LINE 1: create domain d_fail as anyelement;
+                                ^
+create domain d_fail as int4 unique;
+ERROR:  unique constraints not possible for domains
+LINE 1: create domain d_fail as int4 unique;
+                                     ^
+create domain d_fail as int4 PRIMARY key;
+ERROR:  primary key constraints not possible for domains
+LINE 1: create domain d_fail as int4 PRIMARY key;
+                                     ^
+create domain d_fail as int4 constraint cc generated by default as identity;
+ERROR:  specifying GENERATED not supported for domains
+LINE 1: create domain d_fail as int4 constraint cc generated by defa...
+                                     ^
+create domain d_fail as int4 constraint cc generated always as (2) stored;
+ERROR:  specifying GENERATED not supported for domains
+LINE 1: create domain d_fail as int4 constraint cc generated always ...
+                                     ^
+create domain d_fail as int4 constraint cc check(values > 1) no inherit;
+ERROR:  check constraints for domains cannot be marked NO INHERIT
+LINE 1: create domain d_fail as int4 constraint cc check(values > 1)...
+                                     ^
+create domain d_fail as int4 constraint cc check(values > 1) deferrable;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) deferrable...
+                                                             ^
+create domain d_fail as int4 constraint cc check(values > 1) not deferrable;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) not deferr...
+                                                             ^
+create domain d_fail as int4 constraint cc check (value > 1) initially deferred;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check (value > 1) initially ...
+                                                             ^
+create domain d_fail as int4 constraint cc check(values > 1) initially immediate;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) initially ...
+                                                             ^
+create domain d_fail as int4 constraint cc check(values > 1) deferrable not deferrable ;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) deferrable...
+                                                             ^
 -- Test domain input.
 -- Note: the point of checking both INSERT and COPY FROM is that INSERT
 -- exercises CoerceToDomain while COPY exercises domain_in.
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index de56998f5c..3e49b5a18e 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -1024,6 +1024,11 @@ create function xfloat8out(xfloat8) returns cstring immutable strict
 NOTICE:  argument type xfloat8 is only a shell
 LINE 1: create function xfloat8out(xfloat8) returns cstring immutabl...
                                    ^
+--should fail, since type x does not exists.
+create type xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
+ERROR:  type "x" does not exist
+LINE 1: ... xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
+                                                                    ^
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 2a2b777c89..0123f0549d 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -43,6 +43,10 @@ CREATE TABLE itest4 (a int, b text);
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, requires NOT NULL
 ERROR:  column "a" of relation "itest4" must be declared NOT NULL before identity can be added
 ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
+ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS IDENTITY;  -- error, column c does not exists
+ERROR:  column "c" of relation "itest4" does not exist
+LINE 1: ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS ID...
+                                              ^
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- ok
 ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL;  -- error, disallowed
 ERROR:  column "a" of relation "itest4" is an identity column
diff --git a/src/test/regress/expected/typed_table.out b/src/test/regress/expected/typed_table.out
index b6fbda3f21..885f085e15 100644
--- a/src/test/regress/expected/typed_table.out
+++ b/src/test/regress/expected/typed_table.out
@@ -1,5 +1,7 @@
 CREATE TABLE ttable1 OF nothing;
 ERROR:  type "nothing" does not exist
+LINE 1: CREATE TABLE ttable1 OF nothing;
+                                ^
 CREATE TYPE person_type AS (id int, name text);
 CREATE TABLE persons OF person_type;
 CREATE TABLE IF NOT EXISTS persons OF person_type;
@@ -36,6 +38,8 @@ ALTER TABLE persons RENAME COLUMN id TO num;
 ERROR:  cannot rename column of typed table
 ALTER TABLE persons ALTER COLUMN name TYPE varchar;
 ERROR:  cannot alter column type of typed table
+LINE 1: ALTER TABLE persons ALTER COLUMN name TYPE varchar;
+                                         ^
 CREATE TABLE stuff (id int);
 ALTER TABLE persons INHERIT stuff;
 ERROR:  cannot change inheritance of typed table
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 637e3dac38..67c2f89a47 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1985,6 +1985,7 @@ DROP TABLE test_tbl1;
 DROP TYPE test_type1;
 
 CREATE TYPE test_type2 AS (a int, b text);
+CREATE TABLE test_tbl2 OF xx;
 CREATE TABLE test_tbl2 OF test_type2;
 CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2);
 \d test_type2
@@ -2048,6 +2049,7 @@ CREATE TABLE tt6 () INHERITS (tt0);					-- can't have a parent
 CREATE TABLE tt7 (x int, q text, y numeric(8,2));
 ALTER TABLE tt7 DROP q;								-- OK
 
+ALTER TABLE tt0 OF tt_t_noexist;
 ALTER TABLE tt0 OF tt_t0;
 ALTER TABLE tt1 OF tt_t0;
 ALTER TABLE tt2 OF tt_t0;
@@ -2145,6 +2147,11 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
 
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
 SELECT indexrelid::regclass::text as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass ORDER BY 1, 2;
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index ee07b03174..5faa56b31e 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -16,6 +16,27 @@ drop domain domaindroptest cascade;
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 
+-- Test syntax.
+-- create domain error case.
+create domain d_fail as x;
+create domain d_fail as int constraint cc REFERENCES this_table_not_exists(i);
+create domain d_fail as int4 not null no inherit;
+create domain d_fail as int4 not null null;
+create domain d_fail as int4 not null default 3 default 3;
+create domain d_fail int4 DEFAULT 3 + 'h';
+create domain d_fail int4 collate "C";
+create domain d_fail as anyelement;
+
+create domain d_fail as int4 unique;
+create domain d_fail as int4 PRIMARY key;
+create domain d_fail as int4 constraint cc generated by default as identity;
+create domain d_fail as int4 constraint cc generated always as (2) stored;
+create domain d_fail as int4 constraint cc check(values > 1) no inherit;
+create domain d_fail as int4 constraint cc check(values > 1) deferrable;
+create domain d_fail as int4 constraint cc check(values > 1) not deferrable;
+create domain d_fail as int4 constraint cc check (value > 1) initially deferred;
+create domain d_fail as int4 constraint cc check(values > 1) initially immediate;
+create domain d_fail as int4 constraint cc check(values > 1) deferrable not deferrable ;
 
 -- Test domain input.
 
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index 98e9926c9e..d78cbf5f60 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -328,6 +328,8 @@ create function xfloat8in(cstring) returns xfloat8 immutable strict
   language internal as 'int8in';
 create function xfloat8out(xfloat8) returns cstring immutable strict
   language internal as 'int8out';
+--should fail, since type x does not exists.
+create type xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index cb0e05a2f1..5775d6119e 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -19,6 +19,7 @@ SELECT pg_get_serial_sequence('itest1', 'a');
 CREATE TABLE itest4 (a int, b text);
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, requires NOT NULL
 ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
+ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS IDENTITY;  -- error, column c does not exists
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- ok
 ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL;  -- error, disallowed
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, already set
-- 
2.34.1

#8Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: jian he (#7)
Re: Pass ParseState as down to utility functions.

On 2024-Dec-06, jian he wrote:

From 6bf657c3b62b7460b317c42ce2f4fa0988acf1a0 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Fri, 6 Dec 2024 16:37:18 +0800
Subject: [PATCH v6 1/1] print out error position for some DDL command

doing this by passing the source_string to the existing ParseState
or by making a new ParseState passing source_string to it.

With this patch, the following functions will printout the error position for certain error cases.

ATExecAddOf
DefineType
ATPrepAlterColumnType
ATExecAlterColumnType
DefineDomain
AlterType
transformAlterTableStmt

I think it would make more sense to write the commit message in terms of
the DDL commands that now report error position, than the C functions.
Such a list of commands does not need to be exhaustive; a
representative-enough sample probably suffices.

@@ -943,11 +942,13 @@ DefineDomain(CreateDomainStmt *stmt)

if (constr->is_no_inherit)
ereport(ERROR,
-							errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							errmsg("not-null constraints for domains cannot be marked NO INHERIT"));
+							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+							 errmsg("not-null constraints for domains cannot be marked NO INHERIT"),
+							 parser_errposition(pstate, constr->location)));

Once upon a time, ereport() was a simpler macro that did not
use variadic arguments. Back then, the list of functions embedded in it
(errcode, errmsg etc) were forced to be in an additional level of
parentheses so that the macro would work at all (IIRC failure to do that
resulted in strange compile-time problems). This is why a majority of
code is written in the style with those parens. But commit e3a87b4991cc
changed ereport to use __VA_ARGS__, so the auxiliary functions are
actual arguments to errstart() -- which means that the parentheses
you're adding here are unnecessary and discouraged. Just add the
parser_errposition() call and it'll be fine.

--
Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/
"XML!" Exclaimed C++. "What are you doing here? You're not a programming
language."
"Tell that to the people who use me," said XML.
https://burningbird.net/the-parable-of-the-languages/

#9Kirill Reshke
reshkekirill@gmail.com
In reply to: Alvaro Herrera (#8)
1 attachment(s)
Re: Pass ParseState as down to utility functions.

Thank you for reviewing this!

On Fri, 6 Dec 2024 at 19:01, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

I think it would make more sense to write the commit message in terms of
the DDL commands that now report error position, than the C functions.
Such a list of commands does not need to be exhaustive; a
representative-enough sample probably suffices.

Hi! I fixed the commit message as suggested.

Once upon a time, ereport() was a simpler macro that did not
use variadic arguments. Back then, the list of functions embedded in it
(errcode, errmsg etc) were forced to be in an additional level of
parentheses so that the macro would work at all (IIRC failure to do that
resulted in strange compile-time problems). This is why a majority of
code is written in the style with those parens. But commit e3a87b4991cc
changed ereport to use __VA_ARGS__, so the auxiliary functions are
actual arguments to errstart() -- which means that the parentheses
you're adding here are unnecessary and discouraged. Just add the
parser_errposition() call and it'll be fine.

Should be fixed in v7.

--
Best regards,
Kirill Reshke

Attachments:

v7-0001-Print-out-error-position-for-number-of-DDL-comman.patchapplication/octet-stream; name=v7-0001-Print-out-error-position-for-number-of-DDL-comman.patchDownload
From 5dc2074e7939ca58ae0a2b582fb5f008ac842072 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Fri, 6 Dec 2024 16:37:18 +0800
Subject: [PATCH v7] Print out error position for number of DDL commands.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

With this patch, the following functions will printout the error position for certain error cases.
This can be particularly helpful when working with a sequence of DML commands,
such as `create schema create schema_element`.
It also makes it easier to quickly identify the relevant error area in a single DDL command

For example, error positinion reporting is now supported for CREATE
DOMAIN, ALTER TABLE ... ALTER COLUMN ..., ALTER TABLE ... OF ... and
other.
More cases can be found in regression tests.

CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
ERROR:  collations are not supported by type integer
LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
                                      ^

ATExecAlterColumnType code change maybe not necessary, since
ATPrepAlterColumnType will catach most of the error.  AlterType function changes
no effect since struct AlterTypeStmt don't have location info.

ATExecAlterColumnType, AlterType because of the above mentioned reason, don't
have regress test.  all other have tests.

Author: Kirill Reshke <reshkekirill@gmail.com>
Author: Jian He <jian.universality@gmail.com>
Reviewed-By: Michaël Paquier <michael@paquier.xyz>
Reviewed-By: Álvaro Herrera <alvherre@alvh.no-ip.org>

discussion: https://postgr.es/m/CALdSSPhqfvKbDwqJaY=yEePi_aq61GmMpW88i6ZH7CMG_2Z4Cg@mail.gmail.com
---
 src/backend/commands/tablecmds.c              | 43 +++++++----
 src/backend/commands/typecmds.c               | 64 +++++++++-------
 src/backend/parser/parse_utilcmd.c            |  8 +-
 src/backend/tcop/utility.c                    |  4 +-
 src/include/commands/typecmds.h               |  4 +-
 src/test/regress/expected/alter_table.out     | 21 ++++++
 .../regress/expected/collate.icu.utf8.out     |  2 +
 .../regress/expected/collate.linux.utf8.out   |  2 +
 src/test/regress/expected/collate.out         |  2 +
 .../expected/collate.windows.win1252.out      |  2 +
 src/test/regress/expected/domain.out          | 74 +++++++++++++++++++
 src/test/regress/expected/float8.out          |  5 ++
 src/test/regress/expected/identity.out        |  4 +
 src/test/regress/expected/typed_table.out     |  4 +
 src/test/regress/sql/alter_table.sql          |  7 ++
 src/test/regress/sql/domain.sql               | 21 ++++++
 src/test/regress/sql/float8.sql               |  2 +
 src/test/regress/sql/identity.sql             |  1 +
 18 files changed, 222 insertions(+), 48 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6ccae4cb4a8..efa38b14702 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -593,7 +593,8 @@ static void ATPrepAlterColumnType(List **wqueue,
 								  AlterTableUtilityContext *context);
 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-										   AlterTableCmd *cmd, LOCKMODE lockmode);
+										   AlterTableCmd *cmd, LOCKMODE lockmode,
+										   AlterTableUtilityContext *context);
 static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
 											  Relation rel, AttrNumber attnum, const char *colName);
 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
@@ -639,7 +640,9 @@ static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCK
 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
 								   DependencyType deptype);
-static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
+static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename,
+								 LOCKMODE lockmode,
+								 AlterTableUtilityContext *context);
 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
 static void ATExecGenericOptions(Relation rel, List *options);
@@ -5413,7 +5416,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			break;
 		case AT_AlterColumnType:	/* ALTER COLUMN TYPE */
 			/* parse transformation was done earlier */
-			address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
+			address = ATExecAlterColumnType(tab, rel, cmd, lockmode, context);
 			break;
 		case AT_AlterColumnGenericOptions:	/* ALTER COLUMN OPTIONS */
 			address =
@@ -5537,7 +5540,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
 			break;
 		case AT_AddOf:
-			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
+			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode, context);
 			break;
 		case AT_DropOf:
 			ATExecDropOf(rel, lockmode);
@@ -13218,10 +13221,12 @@ ATPrepAlterColumnType(List **wqueue,
 	AclResult	aclresult;
 	bool		is_expr;
 
+	pstate->p_sourcetext = context->queryString;
 	if (rel->rd_rel->reloftype && !recursing)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("cannot alter column type of typed table")));
+				 errmsg("cannot alter column type of typed table"),
+				 parser_errposition(pstate, def->location)));
 
 	/* lookup the attribute so we can check inheritance status */
 	tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
@@ -13237,8 +13242,8 @@ ATPrepAlterColumnType(List **wqueue,
 	if (attnum <= 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot alter system column \"%s\"",
-						colName)));
+				 errmsg("cannot alter system column \"%s\"",colName),
+				 parser_errposition(pstate, def->location)));
 
 	/*
 	 * Cannot specify USING when altering type of a generated column, because
@@ -13271,14 +13276,14 @@ ATPrepAlterColumnType(List **wqueue,
 						colName, RelationGetRelationName(rel))));
 
 	/* Look up the target type */
-	typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
+	typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
 
 	aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error_type(aclresult, targettype);
 
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colName, targettype, targetcollid,
@@ -13537,7 +13542,8 @@ ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
  */
 static ObjectAddress
 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-					  AlterTableCmd *cmd, LOCKMODE lockmode)
+					  AlterTableCmd *cmd, LOCKMODE lockmode,
+					  AlterTableUtilityContext *context)
 {
 	char	   *colName = cmd->name;
 	ColumnDef  *def = (ColumnDef *) cmd->def;
@@ -13558,6 +13564,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	SysScanDesc scan;
 	HeapTuple	depTup;
 	ObjectAddress address;
+	ParseState      *pstate;
+
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
 
 	/*
 	 * Clear all the missing values if we're rewriting the table, since this
@@ -13596,11 +13606,11 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 						colName)));
 
 	/* Look up the target type (should not fail, since prep found it) */
-	typeTuple = typenameType(NULL, typeName, &targettypmod);
+	typeTuple = typenameType(pstate, typeName, &targettypmod);
 	tform = (Form_pg_type) GETSTRUCT(typeTuple);
 	targettype = tform->oid;
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/*
 	 * If there is a default expression for the column, get it and ensure we
@@ -16976,7 +16986,8 @@ drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
  * The address of the type is returned.
  */
 static ObjectAddress
-ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
+ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode,
+			AlterTableUtilityContext *context)
 {
 	Oid			relid = RelationGetRelid(rel);
 	Type		typetuple;
@@ -16993,9 +17004,13 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 	ObjectAddress tableobj,
 				typeobj;
 	HeapTuple	classtuple;
+	ParseState 	*pstate;
 
 	/* Validate the type. */
-	typetuple = typenameType(NULL, ofTypename, NULL);
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
+
+	typetuple = typenameType(pstate, ofTypename, NULL);
 	check_of_type(typetuple);
 	typeform = (Form_pg_type) GETSTRUCT(typetuple);
 	typeid = typeform->oid;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index da591c0922b..0a7cf7fd1c8 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -348,7 +348,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 		Type		likeType;
 		Form_pg_type likeForm;
 
-		likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+		likeType = typenameType(pstate, defGetTypeName(likeTypeEl), NULL);
 		likeForm = (Form_pg_type) GETSTRUCT(likeType);
 		internalLength = likeForm->typlen;
 		byValue = likeForm->typbyval;
@@ -694,7 +694,7 @@ RemoveTypeById(Oid typeOid)
  *		Registers a new domain.
  */
 ObjectAddress
-DefineDomain(CreateDomainStmt *stmt)
+DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
 {
 	char	   *domainName;
 	char	   *domainArrayName;
@@ -761,7 +761,7 @@ DefineDomain(CreateDomainStmt *stmt)
 	/*
 	 * Look up the base type.
 	 */
-	typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
+	typeTup = typenameType(pstate, stmt->typeName, &basetypeMod);
 	baseType = (Form_pg_type) GETSTRUCT(typeTup);
 	basetypeoid = baseType->oid;
 
@@ -783,7 +783,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("\"%s\" is not a valid base type for a domain",
-						TypeNameToString(stmt->typeName))));
+						TypeNameToString(stmt->typeName)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	aclresult = object_aclcheck(TypeRelationId, basetypeoid, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
@@ -809,7 +810,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("collations are not supported by type %s",
-						format_type_be(basetypeoid))));
+						format_type_be(basetypeoid)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	/* passed by value */
 	byValue = baseType->typbyval;
@@ -879,18 +881,15 @@ DefineDomain(CreateDomainStmt *stmt)
 				 */
 				if (saw_default)
 					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("multiple default expressions")));
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("multiple default expressions"),
+							parser_errposition(pstate, constr->location));
 				saw_default = true;
 
 				if (constr->raw_expr)
 				{
-					ParseState *pstate;
 					Node	   *defaultExpr;
 
-					/* Create a dummy ParseState for transformExpr */
-					pstate = make_parsestate(NULL);
-
 					/*
 					 * Cook the constr->raw_expr into an expression. Note:
 					 * name is strictly for error message
@@ -942,12 +941,14 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_NOTNULL:
 				if (nullDefined && !typNotNull)
 					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("conflicting NULL/NOT NULL constraints"),
+							parser_errposition(pstate, constr->location));
 				if (constr->is_no_inherit)
 					ereport(ERROR,
 							errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							errmsg("not-null constraints for domains cannot be marked NO INHERIT"));
+							errmsg("not-null constraints for domains cannot be marked NO INHERIT"),
+							parser_errposition(pstate, constr->location));
 				typNotNull = true;
 				nullDefined = true;
 				break;
@@ -955,8 +956,9 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_NULL:
 				if (nullDefined && typNotNull)
 					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("conflicting NULL/NOT NULL constraints"),
+							parser_errposition(pstate, constr->location));
 				typNotNull = false;
 				nullDefined = true;
 				break;
@@ -971,8 +973,10 @@ DefineDomain(CreateDomainStmt *stmt)
 				 */
 				if (constr->is_no_inherit)
 					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							 errmsg("check constraints for domains cannot be marked NO INHERIT")));
+							errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+							errmsg("check constraints for domains cannot be marked NO INHERIT"),
+							parser_errposition(pstate, constr->location));
+
 				break;
 
 				/*
@@ -980,26 +984,30 @@ DefineDomain(CreateDomainStmt *stmt)
 				 */
 			case CONSTR_UNIQUE:
 				ereport(ERROR,
-						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("unique constraints not possible for domains")));
+						errcode(ERRCODE_SYNTAX_ERROR),
+						errmsg("unique constraints not possible for domains"),
+						parser_errposition(pstate, constr->location));
 				break;
 
 			case CONSTR_PRIMARY:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("primary key constraints not possible for domains")));
+						 errmsg("primary key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_EXCLUSION:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("exclusion constraints not possible for domains")));
+						 errmsg("exclusion constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_FOREIGN:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("foreign key constraints not possible for domains")));
+						 errmsg("foreign key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_ATTR_DEFERRABLE:
@@ -1008,14 +1016,16 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_ATTR_IMMEDIATE:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("specifying constraint deferrability not supported for domains")));
+						 errmsg("specifying constraint deferrability not supported for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_GENERATED:
 			case CONSTR_IDENTITY:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("specifying GENERATED not supported for domains")));
+						 errmsg("specifying GENERATED not supported for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 				/* no default, to let compiler warn about missing case */
@@ -4319,7 +4329,7 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
  * adding new flexibility.
  */
 ObjectAddress
-AlterType(AlterTypeStmt *stmt)
+AlterType(ParseState *pstate, AlterTypeStmt *stmt)
 {
 	ObjectAddress address;
 	Relation	catalog;
@@ -4335,7 +4345,7 @@ AlterType(AlterTypeStmt *stmt)
 
 	/* Make a TypeName so we can use standard type lookup machinery */
 	typename = makeTypeNameFromNameList(stmt->typeName);
-	tup = typenameType(NULL, typename, NULL);
+	tup = typenameType(pstate, typename, NULL);
 
 	typeOid = typeTypeId(tup);
 	typForm = (Form_pg_type) GETSTRUCT(tup);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0f324ee4e31..67a41ac9f12 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1615,7 +1615,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
 
 	Assert(ofTypename);
 
-	tuple = typenameType(NULL, ofTypename, NULL);
+	tuple = typenameType(cxt->pstate, ofTypename, NULL);
 	check_of_type(tuple);
 	ofTypeId = ((Form_pg_type) GETSTRUCT(tuple))->oid;
 	ofTypename->typeOid = ofTypeId; /* cached for later */
@@ -3627,7 +3627,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 							ereport(ERROR,
 									(errcode(ERRCODE_UNDEFINED_COLUMN),
 									 errmsg("column \"%s\" of relation \"%s\" does not exist",
-											cmd->name, RelationGetRelationName(rel))));
+											cmd->name, RelationGetRelationName(rel)),
+									 parser_errposition(pstate, def->location)));
 
 						if (attnum > 0 &&
 							TupleDescAttr(tupdesc, attnum - 1)->attidentity)
@@ -3667,7 +3668,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 						ereport(ERROR,
 								(errcode(ERRCODE_UNDEFINED_COLUMN),
 								 errmsg("column \"%s\" of relation \"%s\" does not exist",
-										cmd->name, RelationGetRelationName(rel))));
+										cmd->name, RelationGetRelationName(rel)),
+								 parser_errposition(pstate, def->location)));
 
 					generateSerialExtraStmts(&cxt, newdef,
 											 get_atttype(relid, attnum),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f28bf371059..aa24283efff 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1712,7 +1712,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_CreateDomainStmt:
-				address = DefineDomain((CreateDomainStmt *) parsetree);
+				address = DefineDomain(pstate, (CreateDomainStmt *) parsetree);
 				break;
 
 			case T_CreateConversionStmt:
@@ -1801,7 +1801,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_AlterTypeStmt:
-				address = AlterType((AlterTypeStmt *) parsetree);
+				address = AlterType(pstate, (AlterTypeStmt *) parsetree);
 				break;
 
 			case T_CommentStmt:
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index e1b02927c4b..6e493b896c2 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -23,7 +23,7 @@
 
 extern ObjectAddress DefineType(ParseState *pstate, List *names, List *parameters);
 extern void RemoveTypeById(Oid typeOid);
-extern ObjectAddress DefineDomain(CreateDomainStmt *stmt);
+extern ObjectAddress DefineDomain(ParseState *pstate, CreateDomainStmt *stmt);
 extern ObjectAddress DefineEnum(CreateEnumStmt *stmt);
 extern ObjectAddress DefineRange(ParseState *pstate, CreateRangeStmt *stmt);
 extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
@@ -58,6 +58,6 @@ extern Oid	AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 									   bool errorOnTableType,
 									   ObjectAddresses *objsMoved);
 
-extern ObjectAddress AlterType(AlterTypeStmt *stmt);
+extern ObjectAddress AlterType(ParseState *pstate, AlterTypeStmt *stmt);
 
 #endif							/* TYPECMDS_H */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 2212c8dbb59..ff64a23a5f1 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3112,6 +3112,10 @@ ERROR:  cannot alter type "test_type1" because column "test_tbl1_idx.row" uses i
 DROP TABLE test_tbl1;
 DROP TYPE test_type1;
 CREATE TYPE test_type2 AS (a int, b text);
+CREATE TABLE test_tbl2 OF xx;
+ERROR:  type "xx" does not exist
+LINE 1: CREATE TABLE test_tbl2 OF xx;
+                                  ^
 CREATE TABLE test_tbl2 OF test_type2;
 CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2);
 \d test_type2
@@ -3263,6 +3267,10 @@ CREATE TABLE tt5 (x int, y numeric(8,2), z int);	-- too few columns
 CREATE TABLE tt6 () INHERITS (tt0);					-- can't have a parent
 CREATE TABLE tt7 (x int, q text, y numeric(8,2));
 ALTER TABLE tt7 DROP q;								-- OK
+ALTER TABLE tt0 OF tt_t_noexist;
+ERROR:  type "tt_t_noexist" does not exist
+LINE 1: ALTER TABLE tt0 OF tt_t_noexist;
+                           ^
 ALTER TABLE tt0 OF tt_t0;
 ALTER TABLE tt1 OF tt_t0;
 ERROR:  table "tt1" has different type for column "y"
@@ -3413,6 +3421,19 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ERROR:  cannot alter system column "xmin"
+LINE 1: ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+                                              ^
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ERROR:  type "x" does not exist
+LINE 1: ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+                                                               ^
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+ERROR:  collations are not supported by type integer
+LINE 1: ...LE comment_test ALTER COLUMN id SET DATA TYPE int collate "C...
+                                                             ^
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
            comment           
diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out
index 6cbadafcfbf..d4f327636fd 100644
--- a/src/test/regress/expected/collate.icu.utf8.out
+++ b/src/test/regress/expected/collate.icu.utf8.out
@@ -120,6 +120,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv-x-icu";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out
index 01664f7c1b5..fbaab7cdf83 100644
--- a/src/test/regress/expected/collate.linux.utf8.out
+++ b/src/test/regress/expected/collate.linux.utf8.out
@@ -122,6 +122,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out
index 593a6226376..bf72908fbd3 100644
--- a/src/test/regress/expected/collate.out
+++ b/src/test/regress/expected/collate.out
@@ -73,6 +73,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "P...
 CREATE DOMAIN testdomain_p AS text COLLATE "POSIX";
 CREATE DOMAIN testdomain_i AS int COLLATE "POSIX"; -- fail
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "POSIX";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_p
diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out
index 31f794988b3..4644f56b31d 100644
--- a/src/test/regress/expected/collate.windows.win1252.out
+++ b/src/test/regress/expected/collate.windows.win1252.out
@@ -124,6 +124,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 42b6559f9c8..222bb54c750 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -15,6 +15,80 @@ NOTICE:  drop cascades to type dependenttypetest
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 ERROR:  type "domaindroptest" does not exist
+-- Test syntax.
+-- create domain error case.
+create domain d_fail as x;
+ERROR:  type "x" does not exist
+LINE 1: create domain d_fail as x;
+                                ^
+create domain d_fail as int constraint cc REFERENCES this_table_not_exists(i);
+ERROR:  foreign key constraints not possible for domains
+LINE 1: create domain d_fail as int constraint cc REFERENCES this_ta...
+                                    ^
+create domain d_fail as int4 not null no inherit;
+ERROR:  not-null constraints for domains cannot be marked NO INHERIT
+LINE 1: create domain d_fail as int4 not null no inherit;
+                                     ^
+create domain d_fail as int4 not null null;
+ERROR:  conflicting NULL/NOT NULL constraints
+LINE 1: create domain d_fail as int4 not null null;
+                                              ^
+create domain d_fail as int4 not null default 3 default 3;
+ERROR:  multiple default expressions
+LINE 1: create domain d_fail as int4 not null default 3 default 3;
+                                                        ^
+create domain d_fail int4 DEFAULT 3 + 'h';
+ERROR:  invalid input syntax for type integer: "h"
+LINE 1: create domain d_fail int4 DEFAULT 3 + 'h';
+                                              ^
+create domain d_fail int4 collate "C";
+ERROR:  collations are not supported by type integer
+LINE 1: create domain d_fail int4 collate "C";
+                             ^
+create domain d_fail as anyelement;
+ERROR:  "anyelement" is not a valid base type for a domain
+LINE 1: create domain d_fail as anyelement;
+                                ^
+create domain d_fail as int4 unique;
+ERROR:  unique constraints not possible for domains
+LINE 1: create domain d_fail as int4 unique;
+                                     ^
+create domain d_fail as int4 PRIMARY key;
+ERROR:  primary key constraints not possible for domains
+LINE 1: create domain d_fail as int4 PRIMARY key;
+                                     ^
+create domain d_fail as int4 constraint cc generated by default as identity;
+ERROR:  specifying GENERATED not supported for domains
+LINE 1: create domain d_fail as int4 constraint cc generated by defa...
+                                     ^
+create domain d_fail as int4 constraint cc generated always as (2) stored;
+ERROR:  specifying GENERATED not supported for domains
+LINE 1: create domain d_fail as int4 constraint cc generated always ...
+                                     ^
+create domain d_fail as int4 constraint cc check(values > 1) no inherit;
+ERROR:  check constraints for domains cannot be marked NO INHERIT
+LINE 1: create domain d_fail as int4 constraint cc check(values > 1)...
+                                     ^
+create domain d_fail as int4 constraint cc check(values > 1) deferrable;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) deferrable...
+                                                             ^
+create domain d_fail as int4 constraint cc check(values > 1) not deferrable;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) not deferr...
+                                                             ^
+create domain d_fail as int4 constraint cc check (value > 1) initially deferred;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check (value > 1) initially ...
+                                                             ^
+create domain d_fail as int4 constraint cc check(values > 1) initially immediate;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) initially ...
+                                                             ^
+create domain d_fail as int4 constraint cc check(values > 1) deferrable not deferrable ;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) deferrable...
+                                                             ^
 -- Test domain input.
 -- Note: the point of checking both INSERT and COPY FROM is that INSERT
 -- exercises CoerceToDomain while COPY exercises domain_in.
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index de56998f5cd..3e49b5a18ef 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -1024,6 +1024,11 @@ create function xfloat8out(xfloat8) returns cstring immutable strict
 NOTICE:  argument type xfloat8 is only a shell
 LINE 1: create function xfloat8out(xfloat8) returns cstring immutabl...
                                    ^
+--should fail, since type x does not exists.
+create type xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
+ERROR:  type "x" does not exist
+LINE 1: ... xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
+                                                                    ^
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 2a2b777c89b..0123f0549d0 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -43,6 +43,10 @@ CREATE TABLE itest4 (a int, b text);
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, requires NOT NULL
 ERROR:  column "a" of relation "itest4" must be declared NOT NULL before identity can be added
 ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
+ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS IDENTITY;  -- error, column c does not exists
+ERROR:  column "c" of relation "itest4" does not exist
+LINE 1: ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS ID...
+                                              ^
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- ok
 ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL;  -- error, disallowed
 ERROR:  column "a" of relation "itest4" is an identity column
diff --git a/src/test/regress/expected/typed_table.out b/src/test/regress/expected/typed_table.out
index b6fbda3f217..885f085e154 100644
--- a/src/test/regress/expected/typed_table.out
+++ b/src/test/regress/expected/typed_table.out
@@ -1,5 +1,7 @@
 CREATE TABLE ttable1 OF nothing;
 ERROR:  type "nothing" does not exist
+LINE 1: CREATE TABLE ttable1 OF nothing;
+                                ^
 CREATE TYPE person_type AS (id int, name text);
 CREATE TABLE persons OF person_type;
 CREATE TABLE IF NOT EXISTS persons OF person_type;
@@ -36,6 +38,8 @@ ALTER TABLE persons RENAME COLUMN id TO num;
 ERROR:  cannot rename column of typed table
 ALTER TABLE persons ALTER COLUMN name TYPE varchar;
 ERROR:  cannot alter column type of typed table
+LINE 1: ALTER TABLE persons ALTER COLUMN name TYPE varchar;
+                                         ^
 CREATE TABLE stuff (id int);
 ALTER TABLE persons INHERIT stuff;
 ERROR:  cannot change inheritance of typed table
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 637e3dac389..67c2f89a47c 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1985,6 +1985,7 @@ DROP TABLE test_tbl1;
 DROP TYPE test_type1;
 
 CREATE TYPE test_type2 AS (a int, b text);
+CREATE TABLE test_tbl2 OF xx;
 CREATE TABLE test_tbl2 OF test_type2;
 CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2);
 \d test_type2
@@ -2048,6 +2049,7 @@ CREATE TABLE tt6 () INHERITS (tt0);					-- can't have a parent
 CREATE TABLE tt7 (x int, q text, y numeric(8,2));
 ALTER TABLE tt7 DROP q;								-- OK
 
+ALTER TABLE tt0 OF tt_t_noexist;
 ALTER TABLE tt0 OF tt_t0;
 ALTER TABLE tt1 OF tt_t0;
 ALTER TABLE tt2 OF tt_t0;
@@ -2145,6 +2147,11 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
 
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
 SELECT indexrelid::regclass::text as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass ORDER BY 1, 2;
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index ee07b03174e..5faa56b31e4 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -16,6 +16,27 @@ drop domain domaindroptest cascade;
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 
+-- Test syntax.
+-- create domain error case.
+create domain d_fail as x;
+create domain d_fail as int constraint cc REFERENCES this_table_not_exists(i);
+create domain d_fail as int4 not null no inherit;
+create domain d_fail as int4 not null null;
+create domain d_fail as int4 not null default 3 default 3;
+create domain d_fail int4 DEFAULT 3 + 'h';
+create domain d_fail int4 collate "C";
+create domain d_fail as anyelement;
+
+create domain d_fail as int4 unique;
+create domain d_fail as int4 PRIMARY key;
+create domain d_fail as int4 constraint cc generated by default as identity;
+create domain d_fail as int4 constraint cc generated always as (2) stored;
+create domain d_fail as int4 constraint cc check(values > 1) no inherit;
+create domain d_fail as int4 constraint cc check(values > 1) deferrable;
+create domain d_fail as int4 constraint cc check(values > 1) not deferrable;
+create domain d_fail as int4 constraint cc check (value > 1) initially deferred;
+create domain d_fail as int4 constraint cc check(values > 1) initially immediate;
+create domain d_fail as int4 constraint cc check(values > 1) deferrable not deferrable ;
 
 -- Test domain input.
 
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index 98e9926c9e0..d78cbf5f60d 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -328,6 +328,8 @@ create function xfloat8in(cstring) returns xfloat8 immutable strict
   language internal as 'int8in';
 create function xfloat8out(xfloat8) returns cstring immutable strict
   language internal as 'int8out';
+--should fail, since type x does not exists.
+create type xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index cb0e05a2f11..5775d6119e4 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -19,6 +19,7 @@ SELECT pg_get_serial_sequence('itest1', 'a');
 CREATE TABLE itest4 (a int, b text);
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, requires NOT NULL
 ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
+ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS IDENTITY;  -- error, column c does not exists
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- ok
 ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL;  -- error, disallowed
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, already set
-- 
2.34.1

#10Michael Paquier
michael@paquier.xyz
In reply to: Kirill Reshke (#9)
Re: Pass ParseState as down to utility functions.

On Mon, Dec 09, 2024 at 12:50:20PM +0500, Kirill Reshke wrote:

Should be fixed in v7.

+create domain d_fail as int4 constraint cc check(values > 1) deferrable;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) deferrable...

I would suggest to split the patch into two pieces for clarity, based
on the fact that your v7 patch is doing more than one thing at the
same time:
- Introduce new tests for the new coverage (domain, CREATE TABLE OF,
ALTER TABLE flavors) in a first patch.
- Introduce the ParseStates in these new code paths in a second patch.

By structuring things this way, it is possible to see what kind of
difference related to the new ParseStates is introduced, based on the
new test coverage introduced in the first patch.

This makes also the whole review easier.

+ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS IDENTITY;
-- error, column c does not exists

Typo here: s/exists/exist/.
--
Michael

#11Kirill Reshke
reshkekirill@gmail.com
In reply to: Michael Paquier (#10)
2 attachment(s)
Re: Pass ParseState as down to utility functions.

On Tue, 10 Dec 2024 at 08:28, Michael Paquier <michael@paquier.xyz> wrote:

I would suggest to split the patch into two pieces for clarity, based
on the fact that your v7 patch is doing more than one thing at the
same time:
- Introduce new tests for the new coverage (domain, CREATE TABLE OF,
ALTER TABLE flavors) in a first patch.
- Introduce the ParseStates in these new code paths in a second patch.

By structuring things this way, it is possible to see what kind of
difference related to the new ParseStates is introduced, based on the
new test coverage introduced in the first patch.

This makes also the whole review easier.

Ok. Sure.

Typo here: s/exists/exist/.

Fixed, Thank you

PFA v8.

--
Best regards,
Kirill Reshke

Attachments:

v8-0001-Add-more-regression-tests-to-various-DDL-patterns.patchapplication/octet-stream; name=v8-0001-Add-more-regression-tests-to-various-DDL-patterns.patchDownload
From 5a45f3aa5690e01e7beabd2b7b67cabbc2ab30d7 Mon Sep 17 00:00:00 2001
From: reshke kirill <reshke@double.cloud>
Date: Tue, 10 Dec 2024 08:39:50 +0000
Subject: [PATCH v8 1/2] Add more regression tests to various DDL patterns
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The main goal of this patch is to increase test coverage of CREATE
DOMAIN, ALTER TABLE ... etc DDL commands.

This is prelimitary patch for enhansing error position reporting feature.

Author: Jian He <jian.universality@gmail.com>
Author: Kirill Reshke <reshkekirill@gmail.com>
Reviewed-By: Michaël Paquier <michael@paquier.xyz>
Reviewed-By: Álvaro Herrera <alvherre@alvh.no-ip.org>

discussion: https://postgr.es/m/CALdSSPhqfvKbDwqJaY=yEePi_aq61GmMpW88i6ZH7CMG_2Z4Cg@mail.gmail.com
---
 src/test/regress/expected/alter_table.out | 11 +++++++
 src/test/regress/expected/domain.out      | 38 +++++++++++++++++++++++
 src/test/regress/expected/float8.out      |  3 ++
 src/test/regress/expected/identity.out    |  2 ++
 src/test/regress/sql/alter_table.sql      |  7 +++++
 src/test/regress/sql/domain.sql           | 21 +++++++++++++
 src/test/regress/sql/float8.sql           |  2 ++
 src/test/regress/sql/identity.sql         |  1 +
 8 files changed, 85 insertions(+)

diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 2212c8dbb59..17802f02e87 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3112,6 +3112,8 @@ ERROR:  cannot alter type "test_type1" because column "test_tbl1_idx.row" uses i
 DROP TABLE test_tbl1;
 DROP TYPE test_type1;
 CREATE TYPE test_type2 AS (a int, b text);
+CREATE TABLE test_tbl2 OF xx;
+ERROR:  type "xx" does not exist
 CREATE TABLE test_tbl2 OF test_type2;
 CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2);
 \d test_type2
@@ -3263,6 +3265,8 @@ CREATE TABLE tt5 (x int, y numeric(8,2), z int);	-- too few columns
 CREATE TABLE tt6 () INHERITS (tt0);					-- can't have a parent
 CREATE TABLE tt7 (x int, q text, y numeric(8,2));
 ALTER TABLE tt7 DROP q;								-- OK
+ALTER TABLE tt0 OF tt_t_noexist;
+ERROR:  type "tt_t_noexist" does not exist
 ALTER TABLE tt0 OF tt_t0;
 ALTER TABLE tt1 OF tt_t0;
 ERROR:  table "tt1" has different type for column "y"
@@ -3413,6 +3417,13 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ERROR:  cannot alter system column "xmin"
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ERROR:  type "x" does not exist
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+ERROR:  collations are not supported by type integer
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
            comment           
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 42b6559f9c8..c5cc41cacdc 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -15,6 +15,44 @@ NOTICE:  drop cascades to type dependenttypetest
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 ERROR:  type "domaindroptest" does not exist
+-- Test syntax.
+-- create domain error case.
+create domain d_fail as x;
+ERROR:  type "x" does not exist
+create domain d_fail as int constraint cc REFERENCES this_table_not_exists(i);
+ERROR:  foreign key constraints not possible for domains
+create domain d_fail as int4 not null no inherit;
+ERROR:  not-null constraints for domains cannot be marked NO INHERIT
+create domain d_fail as int4 not null null;
+ERROR:  conflicting NULL/NOT NULL constraints
+create domain d_fail as int4 not null default 3 default 3;
+ERROR:  multiple default expressions
+create domain d_fail int4 DEFAULT 3 + 'h';
+ERROR:  invalid input syntax for type integer: "h"
+create domain d_fail int4 collate "C";
+ERROR:  collations are not supported by type integer
+create domain d_fail as anyelement;
+ERROR:  "anyelement" is not a valid base type for a domain
+create domain d_fail as int4 unique;
+ERROR:  unique constraints not possible for domains
+create domain d_fail as int4 PRIMARY key;
+ERROR:  primary key constraints not possible for domains
+create domain d_fail as int4 constraint cc generated by default as identity;
+ERROR:  specifying GENERATED not supported for domains
+create domain d_fail as int4 constraint cc generated always as (2) stored;
+ERROR:  specifying GENERATED not supported for domains
+create domain d_fail as int4 constraint cc check(values > 1) no inherit;
+ERROR:  check constraints for domains cannot be marked NO INHERIT
+create domain d_fail as int4 constraint cc check(values > 1) deferrable;
+ERROR:  specifying constraint deferrability not supported for domains
+create domain d_fail as int4 constraint cc check(values > 1) not deferrable;
+ERROR:  specifying constraint deferrability not supported for domains
+create domain d_fail as int4 constraint cc check (value > 1) initially deferred;
+ERROR:  specifying constraint deferrability not supported for domains
+create domain d_fail as int4 constraint cc check(values > 1) initially immediate;
+ERROR:  specifying constraint deferrability not supported for domains
+create domain d_fail as int4 constraint cc check(values > 1) deferrable not deferrable ;
+ERROR:  specifying constraint deferrability not supported for domains
 -- Test domain input.
 -- Note: the point of checking both INSERT and COPY FROM is that INSERT
 -- exercises CoerceToDomain while COPY exercises domain_in.
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index de56998f5cd..1befc471850 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -1024,6 +1024,9 @@ create function xfloat8out(xfloat8) returns cstring immutable strict
 NOTICE:  argument type xfloat8 is only a shell
 LINE 1: create function xfloat8out(xfloat8) returns cstring immutabl...
                                    ^
+--should fail, since type x does not exists.
+create type xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
+ERROR:  type "x" does not exist
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 2a2b777c89b..0398a19484f 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -43,6 +43,8 @@ CREATE TABLE itest4 (a int, b text);
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, requires NOT NULL
 ERROR:  column "a" of relation "itest4" must be declared NOT NULL before identity can be added
 ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
+ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS IDENTITY;  -- error, column c does not exist
+ERROR:  column "c" of relation "itest4" does not exist
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- ok
 ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL;  -- error, disallowed
 ERROR:  column "a" of relation "itest4" is an identity column
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 637e3dac389..67c2f89a47c 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1985,6 +1985,7 @@ DROP TABLE test_tbl1;
 DROP TYPE test_type1;
 
 CREATE TYPE test_type2 AS (a int, b text);
+CREATE TABLE test_tbl2 OF xx;
 CREATE TABLE test_tbl2 OF test_type2;
 CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2);
 \d test_type2
@@ -2048,6 +2049,7 @@ CREATE TABLE tt6 () INHERITS (tt0);					-- can't have a parent
 CREATE TABLE tt7 (x int, q text, y numeric(8,2));
 ALTER TABLE tt7 DROP q;								-- OK
 
+ALTER TABLE tt0 OF tt_t_noexist;
 ALTER TABLE tt0 OF tt_t0;
 ALTER TABLE tt1 OF tt_t0;
 ALTER TABLE tt2 OF tt_t0;
@@ -2145,6 +2147,11 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
 
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
 SELECT indexrelid::regclass::text as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass ORDER BY 1, 2;
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index ee07b03174e..5faa56b31e4 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -16,6 +16,27 @@ drop domain domaindroptest cascade;
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 
+-- Test syntax.
+-- create domain error case.
+create domain d_fail as x;
+create domain d_fail as int constraint cc REFERENCES this_table_not_exists(i);
+create domain d_fail as int4 not null no inherit;
+create domain d_fail as int4 not null null;
+create domain d_fail as int4 not null default 3 default 3;
+create domain d_fail int4 DEFAULT 3 + 'h';
+create domain d_fail int4 collate "C";
+create domain d_fail as anyelement;
+
+create domain d_fail as int4 unique;
+create domain d_fail as int4 PRIMARY key;
+create domain d_fail as int4 constraint cc generated by default as identity;
+create domain d_fail as int4 constraint cc generated always as (2) stored;
+create domain d_fail as int4 constraint cc check(values > 1) no inherit;
+create domain d_fail as int4 constraint cc check(values > 1) deferrable;
+create domain d_fail as int4 constraint cc check(values > 1) not deferrable;
+create domain d_fail as int4 constraint cc check (value > 1) initially deferred;
+create domain d_fail as int4 constraint cc check(values > 1) initially immediate;
+create domain d_fail as int4 constraint cc check(values > 1) deferrable not deferrable ;
 
 -- Test domain input.
 
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index 98e9926c9e0..d78cbf5f60d 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -328,6 +328,8 @@ create function xfloat8in(cstring) returns xfloat8 immutable strict
   language internal as 'int8in';
 create function xfloat8out(xfloat8) returns cstring immutable strict
   language internal as 'int8out';
+--should fail, since type x does not exists.
+create type xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index cb0e05a2f11..45992a3d894 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -19,6 +19,7 @@ SELECT pg_get_serial_sequence('itest1', 'a');
 CREATE TABLE itest4 (a int, b text);
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, requires NOT NULL
 ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
+ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS IDENTITY;  -- error, column c does not exist
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- ok
 ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL;  -- error, disallowed
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, already set
-- 
2.34.1

v8-0002-Print-out-error-position-for-number-of-DDL-comman.patchapplication/octet-stream; name=v8-0002-Print-out-error-position-for-number-of-DDL-comman.patchDownload
From 74be5f888a5af327ca2afb4c23fc3da408d10162 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Fri, 6 Dec 2024 16:37:18 +0800
Subject: [PATCH v8 2/2] Print out error position for number of DDL commands.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

With this patch, the following functions will printout the error position for certain error cases.
This can be particularly helpful when working with a sequence of DML commands,
such as `create schema create schema_element`.
It also makes it easier to quickly identify the relevant error area in a single DDL command

For example, error positinion reporting is now supported for CREATE
DOMAIN, ALTER TABLE ... ALTER COLUMN ..., ALTER TABLE ... OF ... and
other.
More cases can be found in regression tests.

CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
ERROR:  collations are not supported by type integer
LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
                                      ^

ATExecAlterColumnType code change maybe not necessary, since
ATPrepAlterColumnType will catach most of the error.  AlterType function changes
no effect since struct AlterTypeStmt don't have location info.

ATExecAlterColumnType, AlterType because of the above mentioned reason, don't
have regress test.  all other have tests.

Author: Kirill Reshke <reshkekirill@gmail.com>
Author: Jian He <jian.universality@gmail.com>
Reviewed-By: Michaël Paquier <michael@paquier.xyz>
Reviewed-By: Álvaro Herrera <alvherre@alvh.no-ip.org>

discussion: https://postgr.es/m/CALdSSPhqfvKbDwqJaY=yEePi_aq61GmMpW88i6ZH7CMG_2Z4Cg@mail.gmail.com
---
 src/backend/commands/tablecmds.c              | 43 +++++++++----
 src/backend/commands/typecmds.c               | 64 +++++++++++--------
 src/backend/parser/parse_utilcmd.c            |  8 ++-
 src/backend/tcop/utility.c                    |  4 +-
 src/include/commands/typecmds.h               |  4 +-
 src/test/regress/expected/alter_table.out     | 10 +++
 .../regress/expected/collate.icu.utf8.out     |  2 +
 .../regress/expected/collate.linux.utf8.out   |  2 +
 src/test/regress/expected/collate.out         |  2 +
 .../expected/collate.windows.win1252.out      |  2 +
 src/test/regress/expected/domain.out          | 36 +++++++++++
 src/test/regress/expected/float8.out          |  2 +
 src/test/regress/expected/identity.out        |  2 +
 src/test/regress/expected/typed_table.out     |  4 ++
 14 files changed, 137 insertions(+), 48 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6ccae4cb4a8..efa38b14702 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -593,7 +593,8 @@ static void ATPrepAlterColumnType(List **wqueue,
 								  AlterTableUtilityContext *context);
 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-										   AlterTableCmd *cmd, LOCKMODE lockmode);
+										   AlterTableCmd *cmd, LOCKMODE lockmode,
+										   AlterTableUtilityContext *context);
 static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
 											  Relation rel, AttrNumber attnum, const char *colName);
 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
@@ -639,7 +640,9 @@ static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCK
 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
 								   DependencyType deptype);
-static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
+static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename,
+								 LOCKMODE lockmode,
+								 AlterTableUtilityContext *context);
 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
 static void ATExecGenericOptions(Relation rel, List *options);
@@ -5413,7 +5416,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			break;
 		case AT_AlterColumnType:	/* ALTER COLUMN TYPE */
 			/* parse transformation was done earlier */
-			address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
+			address = ATExecAlterColumnType(tab, rel, cmd, lockmode, context);
 			break;
 		case AT_AlterColumnGenericOptions:	/* ALTER COLUMN OPTIONS */
 			address =
@@ -5537,7 +5540,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
 			break;
 		case AT_AddOf:
-			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
+			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode, context);
 			break;
 		case AT_DropOf:
 			ATExecDropOf(rel, lockmode);
@@ -13218,10 +13221,12 @@ ATPrepAlterColumnType(List **wqueue,
 	AclResult	aclresult;
 	bool		is_expr;
 
+	pstate->p_sourcetext = context->queryString;
 	if (rel->rd_rel->reloftype && !recursing)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("cannot alter column type of typed table")));
+				 errmsg("cannot alter column type of typed table"),
+				 parser_errposition(pstate, def->location)));
 
 	/* lookup the attribute so we can check inheritance status */
 	tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
@@ -13237,8 +13242,8 @@ ATPrepAlterColumnType(List **wqueue,
 	if (attnum <= 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot alter system column \"%s\"",
-						colName)));
+				 errmsg("cannot alter system column \"%s\"",colName),
+				 parser_errposition(pstate, def->location)));
 
 	/*
 	 * Cannot specify USING when altering type of a generated column, because
@@ -13271,14 +13276,14 @@ ATPrepAlterColumnType(List **wqueue,
 						colName, RelationGetRelationName(rel))));
 
 	/* Look up the target type */
-	typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
+	typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
 
 	aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error_type(aclresult, targettype);
 
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colName, targettype, targetcollid,
@@ -13537,7 +13542,8 @@ ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
  */
 static ObjectAddress
 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-					  AlterTableCmd *cmd, LOCKMODE lockmode)
+					  AlterTableCmd *cmd, LOCKMODE lockmode,
+					  AlterTableUtilityContext *context)
 {
 	char	   *colName = cmd->name;
 	ColumnDef  *def = (ColumnDef *) cmd->def;
@@ -13558,6 +13564,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	SysScanDesc scan;
 	HeapTuple	depTup;
 	ObjectAddress address;
+	ParseState      *pstate;
+
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
 
 	/*
 	 * Clear all the missing values if we're rewriting the table, since this
@@ -13596,11 +13606,11 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 						colName)));
 
 	/* Look up the target type (should not fail, since prep found it) */
-	typeTuple = typenameType(NULL, typeName, &targettypmod);
+	typeTuple = typenameType(pstate, typeName, &targettypmod);
 	tform = (Form_pg_type) GETSTRUCT(typeTuple);
 	targettype = tform->oid;
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/*
 	 * If there is a default expression for the column, get it and ensure we
@@ -16976,7 +16986,8 @@ drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
  * The address of the type is returned.
  */
 static ObjectAddress
-ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
+ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode,
+			AlterTableUtilityContext *context)
 {
 	Oid			relid = RelationGetRelid(rel);
 	Type		typetuple;
@@ -16993,9 +17004,13 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 	ObjectAddress tableobj,
 				typeobj;
 	HeapTuple	classtuple;
+	ParseState 	*pstate;
 
 	/* Validate the type. */
-	typetuple = typenameType(NULL, ofTypename, NULL);
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
+
+	typetuple = typenameType(pstate, ofTypename, NULL);
 	check_of_type(typetuple);
 	typeform = (Form_pg_type) GETSTRUCT(typetuple);
 	typeid = typeform->oid;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index da591c0922b..0a7cf7fd1c8 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -348,7 +348,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 		Type		likeType;
 		Form_pg_type likeForm;
 
-		likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+		likeType = typenameType(pstate, defGetTypeName(likeTypeEl), NULL);
 		likeForm = (Form_pg_type) GETSTRUCT(likeType);
 		internalLength = likeForm->typlen;
 		byValue = likeForm->typbyval;
@@ -694,7 +694,7 @@ RemoveTypeById(Oid typeOid)
  *		Registers a new domain.
  */
 ObjectAddress
-DefineDomain(CreateDomainStmt *stmt)
+DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
 {
 	char	   *domainName;
 	char	   *domainArrayName;
@@ -761,7 +761,7 @@ DefineDomain(CreateDomainStmt *stmt)
 	/*
 	 * Look up the base type.
 	 */
-	typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
+	typeTup = typenameType(pstate, stmt->typeName, &basetypeMod);
 	baseType = (Form_pg_type) GETSTRUCT(typeTup);
 	basetypeoid = baseType->oid;
 
@@ -783,7 +783,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("\"%s\" is not a valid base type for a domain",
-						TypeNameToString(stmt->typeName))));
+						TypeNameToString(stmt->typeName)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	aclresult = object_aclcheck(TypeRelationId, basetypeoid, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
@@ -809,7 +810,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("collations are not supported by type %s",
-						format_type_be(basetypeoid))));
+						format_type_be(basetypeoid)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	/* passed by value */
 	byValue = baseType->typbyval;
@@ -879,18 +881,15 @@ DefineDomain(CreateDomainStmt *stmt)
 				 */
 				if (saw_default)
 					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("multiple default expressions")));
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("multiple default expressions"),
+							parser_errposition(pstate, constr->location));
 				saw_default = true;
 
 				if (constr->raw_expr)
 				{
-					ParseState *pstate;
 					Node	   *defaultExpr;
 
-					/* Create a dummy ParseState for transformExpr */
-					pstate = make_parsestate(NULL);
-
 					/*
 					 * Cook the constr->raw_expr into an expression. Note:
 					 * name is strictly for error message
@@ -942,12 +941,14 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_NOTNULL:
 				if (nullDefined && !typNotNull)
 					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("conflicting NULL/NOT NULL constraints"),
+							parser_errposition(pstate, constr->location));
 				if (constr->is_no_inherit)
 					ereport(ERROR,
 							errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							errmsg("not-null constraints for domains cannot be marked NO INHERIT"));
+							errmsg("not-null constraints for domains cannot be marked NO INHERIT"),
+							parser_errposition(pstate, constr->location));
 				typNotNull = true;
 				nullDefined = true;
 				break;
@@ -955,8 +956,9 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_NULL:
 				if (nullDefined && typNotNull)
 					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("conflicting NULL/NOT NULL constraints"),
+							parser_errposition(pstate, constr->location));
 				typNotNull = false;
 				nullDefined = true;
 				break;
@@ -971,8 +973,10 @@ DefineDomain(CreateDomainStmt *stmt)
 				 */
 				if (constr->is_no_inherit)
 					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							 errmsg("check constraints for domains cannot be marked NO INHERIT")));
+							errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+							errmsg("check constraints for domains cannot be marked NO INHERIT"),
+							parser_errposition(pstate, constr->location));
+
 				break;
 
 				/*
@@ -980,26 +984,30 @@ DefineDomain(CreateDomainStmt *stmt)
 				 */
 			case CONSTR_UNIQUE:
 				ereport(ERROR,
-						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("unique constraints not possible for domains")));
+						errcode(ERRCODE_SYNTAX_ERROR),
+						errmsg("unique constraints not possible for domains"),
+						parser_errposition(pstate, constr->location));
 				break;
 
 			case CONSTR_PRIMARY:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("primary key constraints not possible for domains")));
+						 errmsg("primary key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_EXCLUSION:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("exclusion constraints not possible for domains")));
+						 errmsg("exclusion constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_FOREIGN:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("foreign key constraints not possible for domains")));
+						 errmsg("foreign key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_ATTR_DEFERRABLE:
@@ -1008,14 +1016,16 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_ATTR_IMMEDIATE:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("specifying constraint deferrability not supported for domains")));
+						 errmsg("specifying constraint deferrability not supported for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_GENERATED:
 			case CONSTR_IDENTITY:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("specifying GENERATED not supported for domains")));
+						 errmsg("specifying GENERATED not supported for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 				/* no default, to let compiler warn about missing case */
@@ -4319,7 +4329,7 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
  * adding new flexibility.
  */
 ObjectAddress
-AlterType(AlterTypeStmt *stmt)
+AlterType(ParseState *pstate, AlterTypeStmt *stmt)
 {
 	ObjectAddress address;
 	Relation	catalog;
@@ -4335,7 +4345,7 @@ AlterType(AlterTypeStmt *stmt)
 
 	/* Make a TypeName so we can use standard type lookup machinery */
 	typename = makeTypeNameFromNameList(stmt->typeName);
-	tup = typenameType(NULL, typename, NULL);
+	tup = typenameType(pstate, typename, NULL);
 
 	typeOid = typeTypeId(tup);
 	typForm = (Form_pg_type) GETSTRUCT(tup);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0f324ee4e31..67a41ac9f12 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1615,7 +1615,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
 
 	Assert(ofTypename);
 
-	tuple = typenameType(NULL, ofTypename, NULL);
+	tuple = typenameType(cxt->pstate, ofTypename, NULL);
 	check_of_type(tuple);
 	ofTypeId = ((Form_pg_type) GETSTRUCT(tuple))->oid;
 	ofTypename->typeOid = ofTypeId; /* cached for later */
@@ -3627,7 +3627,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 							ereport(ERROR,
 									(errcode(ERRCODE_UNDEFINED_COLUMN),
 									 errmsg("column \"%s\" of relation \"%s\" does not exist",
-											cmd->name, RelationGetRelationName(rel))));
+											cmd->name, RelationGetRelationName(rel)),
+									 parser_errposition(pstate, def->location)));
 
 						if (attnum > 0 &&
 							TupleDescAttr(tupdesc, attnum - 1)->attidentity)
@@ -3667,7 +3668,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 						ereport(ERROR,
 								(errcode(ERRCODE_UNDEFINED_COLUMN),
 								 errmsg("column \"%s\" of relation \"%s\" does not exist",
-										cmd->name, RelationGetRelationName(rel))));
+										cmd->name, RelationGetRelationName(rel)),
+								 parser_errposition(pstate, def->location)));
 
 					generateSerialExtraStmts(&cxt, newdef,
 											 get_atttype(relid, attnum),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f28bf371059..aa24283efff 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1712,7 +1712,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_CreateDomainStmt:
-				address = DefineDomain((CreateDomainStmt *) parsetree);
+				address = DefineDomain(pstate, (CreateDomainStmt *) parsetree);
 				break;
 
 			case T_CreateConversionStmt:
@@ -1801,7 +1801,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_AlterTypeStmt:
-				address = AlterType((AlterTypeStmt *) parsetree);
+				address = AlterType(pstate, (AlterTypeStmt *) parsetree);
 				break;
 
 			case T_CommentStmt:
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index e1b02927c4b..6e493b896c2 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -23,7 +23,7 @@
 
 extern ObjectAddress DefineType(ParseState *pstate, List *names, List *parameters);
 extern void RemoveTypeById(Oid typeOid);
-extern ObjectAddress DefineDomain(CreateDomainStmt *stmt);
+extern ObjectAddress DefineDomain(ParseState *pstate, CreateDomainStmt *stmt);
 extern ObjectAddress DefineEnum(CreateEnumStmt *stmt);
 extern ObjectAddress DefineRange(ParseState *pstate, CreateRangeStmt *stmt);
 extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
@@ -58,6 +58,6 @@ extern Oid	AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 									   bool errorOnTableType,
 									   ObjectAddresses *objsMoved);
 
-extern ObjectAddress AlterType(AlterTypeStmt *stmt);
+extern ObjectAddress AlterType(ParseState *pstate, AlterTypeStmt *stmt);
 
 #endif							/* TYPECMDS_H */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 17802f02e87..ff64a23a5f1 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3114,6 +3114,8 @@ DROP TYPE test_type1;
 CREATE TYPE test_type2 AS (a int, b text);
 CREATE TABLE test_tbl2 OF xx;
 ERROR:  type "xx" does not exist
+LINE 1: CREATE TABLE test_tbl2 OF xx;
+                                  ^
 CREATE TABLE test_tbl2 OF test_type2;
 CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2);
 \d test_type2
@@ -3267,6 +3269,8 @@ CREATE TABLE tt7 (x int, q text, y numeric(8,2));
 ALTER TABLE tt7 DROP q;								-- OK
 ALTER TABLE tt0 OF tt_t_noexist;
 ERROR:  type "tt_t_noexist" does not exist
+LINE 1: ALTER TABLE tt0 OF tt_t_noexist;
+                           ^
 ALTER TABLE tt0 OF tt_t0;
 ALTER TABLE tt1 OF tt_t0;
 ERROR:  table "tt1" has different type for column "y"
@@ -3420,10 +3424,16 @@ ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
 --test error report position
 ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
 ERROR:  cannot alter system column "xmin"
+LINE 1: ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+                                              ^
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
 ERROR:  type "x" does not exist
+LINE 1: ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+                                                               ^
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
 ERROR:  collations are not supported by type integer
+LINE 1: ...LE comment_test ALTER COLUMN id SET DATA TYPE int collate "C...
+                                                             ^
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
            comment           
diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out
index 6cbadafcfbf..d4f327636fd 100644
--- a/src/test/regress/expected/collate.icu.utf8.out
+++ b/src/test/regress/expected/collate.icu.utf8.out
@@ -120,6 +120,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv-x-icu";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out
index 01664f7c1b5..fbaab7cdf83 100644
--- a/src/test/regress/expected/collate.linux.utf8.out
+++ b/src/test/regress/expected/collate.linux.utf8.out
@@ -122,6 +122,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out
index 593a6226376..bf72908fbd3 100644
--- a/src/test/regress/expected/collate.out
+++ b/src/test/regress/expected/collate.out
@@ -73,6 +73,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "P...
 CREATE DOMAIN testdomain_p AS text COLLATE "POSIX";
 CREATE DOMAIN testdomain_i AS int COLLATE "POSIX"; -- fail
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "POSIX";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_p
diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out
index 31f794988b3..4644f56b31d 100644
--- a/src/test/regress/expected/collate.windows.win1252.out
+++ b/src/test/regress/expected/collate.windows.win1252.out
@@ -124,6 +124,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index c5cc41cacdc..222bb54c750 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -19,40 +19,76 @@ ERROR:  type "domaindroptest" does not exist
 -- create domain error case.
 create domain d_fail as x;
 ERROR:  type "x" does not exist
+LINE 1: create domain d_fail as x;
+                                ^
 create domain d_fail as int constraint cc REFERENCES this_table_not_exists(i);
 ERROR:  foreign key constraints not possible for domains
+LINE 1: create domain d_fail as int constraint cc REFERENCES this_ta...
+                                    ^
 create domain d_fail as int4 not null no inherit;
 ERROR:  not-null constraints for domains cannot be marked NO INHERIT
+LINE 1: create domain d_fail as int4 not null no inherit;
+                                     ^
 create domain d_fail as int4 not null null;
 ERROR:  conflicting NULL/NOT NULL constraints
+LINE 1: create domain d_fail as int4 not null null;
+                                              ^
 create domain d_fail as int4 not null default 3 default 3;
 ERROR:  multiple default expressions
+LINE 1: create domain d_fail as int4 not null default 3 default 3;
+                                                        ^
 create domain d_fail int4 DEFAULT 3 + 'h';
 ERROR:  invalid input syntax for type integer: "h"
+LINE 1: create domain d_fail int4 DEFAULT 3 + 'h';
+                                              ^
 create domain d_fail int4 collate "C";
 ERROR:  collations are not supported by type integer
+LINE 1: create domain d_fail int4 collate "C";
+                             ^
 create domain d_fail as anyelement;
 ERROR:  "anyelement" is not a valid base type for a domain
+LINE 1: create domain d_fail as anyelement;
+                                ^
 create domain d_fail as int4 unique;
 ERROR:  unique constraints not possible for domains
+LINE 1: create domain d_fail as int4 unique;
+                                     ^
 create domain d_fail as int4 PRIMARY key;
 ERROR:  primary key constraints not possible for domains
+LINE 1: create domain d_fail as int4 PRIMARY key;
+                                     ^
 create domain d_fail as int4 constraint cc generated by default as identity;
 ERROR:  specifying GENERATED not supported for domains
+LINE 1: create domain d_fail as int4 constraint cc generated by defa...
+                                     ^
 create domain d_fail as int4 constraint cc generated always as (2) stored;
 ERROR:  specifying GENERATED not supported for domains
+LINE 1: create domain d_fail as int4 constraint cc generated always ...
+                                     ^
 create domain d_fail as int4 constraint cc check(values > 1) no inherit;
 ERROR:  check constraints for domains cannot be marked NO INHERIT
+LINE 1: create domain d_fail as int4 constraint cc check(values > 1)...
+                                     ^
 create domain d_fail as int4 constraint cc check(values > 1) deferrable;
 ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) deferrable...
+                                                             ^
 create domain d_fail as int4 constraint cc check(values > 1) not deferrable;
 ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) not deferr...
+                                                             ^
 create domain d_fail as int4 constraint cc check (value > 1) initially deferred;
 ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check (value > 1) initially ...
+                                                             ^
 create domain d_fail as int4 constraint cc check(values > 1) initially immediate;
 ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) initially ...
+                                                             ^
 create domain d_fail as int4 constraint cc check(values > 1) deferrable not deferrable ;
 ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) deferrable...
+                                                             ^
 -- Test domain input.
 -- Note: the point of checking both INSERT and COPY FROM is that INSERT
 -- exercises CoerceToDomain while COPY exercises domain_in.
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index 1befc471850..3e49b5a18ef 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -1027,6 +1027,8 @@ LINE 1: create function xfloat8out(xfloat8) returns cstring immutabl...
 --should fail, since type x does not exists.
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
 ERROR:  type "x" does not exist
+LINE 1: ... xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
+                                                                    ^
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 0398a19484f..0b370235ea9 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -45,6 +45,8 @@ ERROR:  column "a" of relation "itest4" must be declared NOT NULL before identit
 ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
 ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS IDENTITY;  -- error, column c does not exist
 ERROR:  column "c" of relation "itest4" does not exist
+LINE 1: ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS ID...
+                                              ^
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- ok
 ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL;  -- error, disallowed
 ERROR:  column "a" of relation "itest4" is an identity column
diff --git a/src/test/regress/expected/typed_table.out b/src/test/regress/expected/typed_table.out
index b6fbda3f217..885f085e154 100644
--- a/src/test/regress/expected/typed_table.out
+++ b/src/test/regress/expected/typed_table.out
@@ -1,5 +1,7 @@
 CREATE TABLE ttable1 OF nothing;
 ERROR:  type "nothing" does not exist
+LINE 1: CREATE TABLE ttable1 OF nothing;
+                                ^
 CREATE TYPE person_type AS (id int, name text);
 CREATE TABLE persons OF person_type;
 CREATE TABLE IF NOT EXISTS persons OF person_type;
@@ -36,6 +38,8 @@ ALTER TABLE persons RENAME COLUMN id TO num;
 ERROR:  cannot rename column of typed table
 ALTER TABLE persons ALTER COLUMN name TYPE varchar;
 ERROR:  cannot alter column type of typed table
+LINE 1: ALTER TABLE persons ALTER COLUMN name TYPE varchar;
+                                         ^
 CREATE TABLE stuff (id int);
 ALTER TABLE persons INHERIT stuff;
 ERROR:  cannot change inheritance of typed table
-- 
2.34.1

#12jian he
jian.universality@gmail.com
In reply to: Kirill Reshke (#11)
2 attachment(s)
Re: Pass ParseState as down to utility functions.

add parser_errposition to some places in
transformTableConstraint, transformColumnDefinition
where v8 didn't.

Attachments:

v9-0002-Print-out-error-position-for-number-of-DDL-comman.patchtext/x-patch; charset=UTF-8; name=v9-0002-Print-out-error-position-for-number-of-DDL-comman.patchDownload
From b49e1b74b5d479b854599c0f9d6b6df1e61aa67c Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Tue, 10 Dec 2024 22:36:45 +0800
Subject: [PATCH v9 2/2] Print out error position for number of DDL commands.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

With this patch, the following functions will printout the error position for certain error cases.
This can be particularly helpful when working with a sequence of DML commands,
such as `create schema create schema_element`.
It also makes it easier to quickly identify the relevant error area in a single DDL command

For example, error positinion reporting is now supported for CREATE
DOMAIN, ALTER TABLE ... ALTER COLUMN ..., ALTER TABLE ... OF ... and
other.
More cases can be found in regression tests.

CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
ERROR:  collations are not supported by type integer
LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
                                      ^

ATExecAlterColumnType code change maybe not necessary, since
ATPrepAlterColumnType will catach most of the error.  AlterType function changes
no effect since struct AlterTypeStmt don't have location info.

ATExecAlterColumnType, AlterType because of the above mentioned reason, don't
have regress test.  all other have tests.

Author: Kirill Reshke <reshkekirill@gmail.com>
Author: Jian He <jian.universality@gmail.com>
Reviewed-By: Michaël Paquier <michael@paquier.xyz>
Reviewed-By: Álvaro Herrera <alvherre@alvh.no-ip.org>

discussion: https://postgr.es/m/CALdSSPhqfvKbDwqJaY=yEePi_aq61GmMpW88i6ZH7CMG_2Z4Cg@mail.gmail.com
---
 src/backend/commands/tablecmds.c              | 43 +++++++++----
 src/backend/commands/typecmds.c               | 64 +++++++++++--------
 src/backend/parser/parse_utilcmd.c            | 21 ++++--
 src/backend/tcop/utility.c                    |  4 +-
 src/include/commands/typecmds.h               |  4 +-
 src/test/regress/expected/alter_table.out     | 10 +++
 .../regress/expected/collate.icu.utf8.out     |  2 +
 .../regress/expected/collate.linux.utf8.out   |  2 +
 src/test/regress/expected/collate.out         |  2 +
 .../expected/collate.windows.win1252.out      |  2 +
 src/test/regress/expected/constraints.out     | 14 ++++
 src/test/regress/expected/domain.out          | 36 +++++++++++
 src/test/regress/expected/float8.out          |  2 +
 src/test/regress/expected/identity.out        |  2 +
 src/test/regress/expected/typed_table.out     |  4 ++
 15 files changed, 160 insertions(+), 52 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6ccae4cb4a..efa38b1470 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -593,7 +593,8 @@ static void ATPrepAlterColumnType(List **wqueue,
 								  AlterTableUtilityContext *context);
 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-										   AlterTableCmd *cmd, LOCKMODE lockmode);
+										   AlterTableCmd *cmd, LOCKMODE lockmode,
+										   AlterTableUtilityContext *context);
 static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
 											  Relation rel, AttrNumber attnum, const char *colName);
 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
@@ -639,7 +640,9 @@ static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCK
 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
 								   DependencyType deptype);
-static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
+static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename,
+								 LOCKMODE lockmode,
+								 AlterTableUtilityContext *context);
 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
 static void ATExecGenericOptions(Relation rel, List *options);
@@ -5413,7 +5416,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			break;
 		case AT_AlterColumnType:	/* ALTER COLUMN TYPE */
 			/* parse transformation was done earlier */
-			address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
+			address = ATExecAlterColumnType(tab, rel, cmd, lockmode, context);
 			break;
 		case AT_AlterColumnGenericOptions:	/* ALTER COLUMN OPTIONS */
 			address =
@@ -5537,7 +5540,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
 			break;
 		case AT_AddOf:
-			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
+			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode, context);
 			break;
 		case AT_DropOf:
 			ATExecDropOf(rel, lockmode);
@@ -13218,10 +13221,12 @@ ATPrepAlterColumnType(List **wqueue,
 	AclResult	aclresult;
 	bool		is_expr;
 
+	pstate->p_sourcetext = context->queryString;
 	if (rel->rd_rel->reloftype && !recursing)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("cannot alter column type of typed table")));
+				 errmsg("cannot alter column type of typed table"),
+				 parser_errposition(pstate, def->location)));
 
 	/* lookup the attribute so we can check inheritance status */
 	tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
@@ -13237,8 +13242,8 @@ ATPrepAlterColumnType(List **wqueue,
 	if (attnum <= 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot alter system column \"%s\"",
-						colName)));
+				 errmsg("cannot alter system column \"%s\"",colName),
+				 parser_errposition(pstate, def->location)));
 
 	/*
 	 * Cannot specify USING when altering type of a generated column, because
@@ -13271,14 +13276,14 @@ ATPrepAlterColumnType(List **wqueue,
 						colName, RelationGetRelationName(rel))));
 
 	/* Look up the target type */
-	typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
+	typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
 
 	aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error_type(aclresult, targettype);
 
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colName, targettype, targetcollid,
@@ -13537,7 +13542,8 @@ ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
  */
 static ObjectAddress
 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-					  AlterTableCmd *cmd, LOCKMODE lockmode)
+					  AlterTableCmd *cmd, LOCKMODE lockmode,
+					  AlterTableUtilityContext *context)
 {
 	char	   *colName = cmd->name;
 	ColumnDef  *def = (ColumnDef *) cmd->def;
@@ -13558,6 +13564,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	SysScanDesc scan;
 	HeapTuple	depTup;
 	ObjectAddress address;
+	ParseState      *pstate;
+
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
 
 	/*
 	 * Clear all the missing values if we're rewriting the table, since this
@@ -13596,11 +13606,11 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 						colName)));
 
 	/* Look up the target type (should not fail, since prep found it) */
-	typeTuple = typenameType(NULL, typeName, &targettypmod);
+	typeTuple = typenameType(pstate, typeName, &targettypmod);
 	tform = (Form_pg_type) GETSTRUCT(typeTuple);
 	targettype = tform->oid;
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/*
 	 * If there is a default expression for the column, get it and ensure we
@@ -16976,7 +16986,8 @@ drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
  * The address of the type is returned.
  */
 static ObjectAddress
-ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
+ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode,
+			AlterTableUtilityContext *context)
 {
 	Oid			relid = RelationGetRelid(rel);
 	Type		typetuple;
@@ -16993,9 +17004,13 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 	ObjectAddress tableobj,
 				typeobj;
 	HeapTuple	classtuple;
+	ParseState 	*pstate;
 
 	/* Validate the type. */
-	typetuple = typenameType(NULL, ofTypename, NULL);
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
+
+	typetuple = typenameType(pstate, ofTypename, NULL);
 	check_of_type(typetuple);
 	typeform = (Form_pg_type) GETSTRUCT(typetuple);
 	typeid = typeform->oid;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index da591c0922..0a7cf7fd1c 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -348,7 +348,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 		Type		likeType;
 		Form_pg_type likeForm;
 
-		likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+		likeType = typenameType(pstate, defGetTypeName(likeTypeEl), NULL);
 		likeForm = (Form_pg_type) GETSTRUCT(likeType);
 		internalLength = likeForm->typlen;
 		byValue = likeForm->typbyval;
@@ -694,7 +694,7 @@ RemoveTypeById(Oid typeOid)
  *		Registers a new domain.
  */
 ObjectAddress
-DefineDomain(CreateDomainStmt *stmt)
+DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
 {
 	char	   *domainName;
 	char	   *domainArrayName;
@@ -761,7 +761,7 @@ DefineDomain(CreateDomainStmt *stmt)
 	/*
 	 * Look up the base type.
 	 */
-	typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
+	typeTup = typenameType(pstate, stmt->typeName, &basetypeMod);
 	baseType = (Form_pg_type) GETSTRUCT(typeTup);
 	basetypeoid = baseType->oid;
 
@@ -783,7 +783,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("\"%s\" is not a valid base type for a domain",
-						TypeNameToString(stmt->typeName))));
+						TypeNameToString(stmt->typeName)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	aclresult = object_aclcheck(TypeRelationId, basetypeoid, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
@@ -809,7 +810,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("collations are not supported by type %s",
-						format_type_be(basetypeoid))));
+						format_type_be(basetypeoid)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	/* passed by value */
 	byValue = baseType->typbyval;
@@ -879,18 +881,15 @@ DefineDomain(CreateDomainStmt *stmt)
 				 */
 				if (saw_default)
 					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("multiple default expressions")));
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("multiple default expressions"),
+							parser_errposition(pstate, constr->location));
 				saw_default = true;
 
 				if (constr->raw_expr)
 				{
-					ParseState *pstate;
 					Node	   *defaultExpr;
 
-					/* Create a dummy ParseState for transformExpr */
-					pstate = make_parsestate(NULL);
-
 					/*
 					 * Cook the constr->raw_expr into an expression. Note:
 					 * name is strictly for error message
@@ -942,12 +941,14 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_NOTNULL:
 				if (nullDefined && !typNotNull)
 					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("conflicting NULL/NOT NULL constraints"),
+							parser_errposition(pstate, constr->location));
 				if (constr->is_no_inherit)
 					ereport(ERROR,
 							errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							errmsg("not-null constraints for domains cannot be marked NO INHERIT"));
+							errmsg("not-null constraints for domains cannot be marked NO INHERIT"),
+							parser_errposition(pstate, constr->location));
 				typNotNull = true;
 				nullDefined = true;
 				break;
@@ -955,8 +956,9 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_NULL:
 				if (nullDefined && typNotNull)
 					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("conflicting NULL/NOT NULL constraints"),
+							parser_errposition(pstate, constr->location));
 				typNotNull = false;
 				nullDefined = true;
 				break;
@@ -971,8 +973,10 @@ DefineDomain(CreateDomainStmt *stmt)
 				 */
 				if (constr->is_no_inherit)
 					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							 errmsg("check constraints for domains cannot be marked NO INHERIT")));
+							errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+							errmsg("check constraints for domains cannot be marked NO INHERIT"),
+							parser_errposition(pstate, constr->location));
+
 				break;
 
 				/*
@@ -980,26 +984,30 @@ DefineDomain(CreateDomainStmt *stmt)
 				 */
 			case CONSTR_UNIQUE:
 				ereport(ERROR,
-						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("unique constraints not possible for domains")));
+						errcode(ERRCODE_SYNTAX_ERROR),
+						errmsg("unique constraints not possible for domains"),
+						parser_errposition(pstate, constr->location));
 				break;
 
 			case CONSTR_PRIMARY:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("primary key constraints not possible for domains")));
+						 errmsg("primary key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_EXCLUSION:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("exclusion constraints not possible for domains")));
+						 errmsg("exclusion constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_FOREIGN:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("foreign key constraints not possible for domains")));
+						 errmsg("foreign key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_ATTR_DEFERRABLE:
@@ -1008,14 +1016,16 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_ATTR_IMMEDIATE:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("specifying constraint deferrability not supported for domains")));
+						 errmsg("specifying constraint deferrability not supported for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_GENERATED:
 			case CONSTR_IDENTITY:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("specifying GENERATED not supported for domains")));
+						 errmsg("specifying GENERATED not supported for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 				/* no default, to let compiler warn about missing case */
@@ -4319,7 +4329,7 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
  * adding new flexibility.
  */
 ObjectAddress
-AlterType(AlterTypeStmt *stmt)
+AlterType(ParseState *pstate, AlterTypeStmt *stmt)
 {
 	ObjectAddress address;
 	Relation	catalog;
@@ -4335,7 +4345,7 @@ AlterType(AlterTypeStmt *stmt)
 
 	/* Make a TypeName so we can use standard type lookup machinery */
 	typename = makeTypeNameFromNameList(stmt->typeName);
-	tup = typenameType(NULL, typename, NULL);
+	tup = typenameType(pstate, typename, NULL);
 
 	typeOid = typeTypeId(tup);
 	typForm = (Form_pg_type) GETSTRUCT(tup);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0f324ee4e3..7223911bf1 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -756,7 +756,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 				if (cxt->ispartitioned && constraint->is_no_inherit)
 					ereport(ERROR,
 							errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-							errmsg("not-null constraints on partitioned tables cannot be NO INHERIT"));
+							errmsg("not-null constraints on partitioned tables cannot be NO INHERIT"),
+							parser_errposition(cxt->pstate, constraint->location));
 
 				/* Disallow conflicting [NOT] NULL markings */
 				if (saw_nullable && !column->is_not_null)
@@ -771,7 +772,9 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 					ereport(ERROR,
 							errcode(ERRCODE_SYNTAX_ERROR),
 							errmsg("conflicting NO INHERIT declarations for not-null constraints on column \"%s\"",
-								   column->colname));
+								   column->colname),
+							parser_errposition(cxt->pstate, constraint->location));
+
 
 				/*
 				 * If this is the first time we see this column being marked
@@ -806,7 +809,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 						ereport(ERROR,
 								errcode(ERRCODE_SYNTAX_ERROR),
 								errmsg("conflicting NO INHERIT declarations for not-null constraints on column \"%s\"",
-									   column->colname));
+									   column->colname),
+								parser_errposition(cxt->pstate, constraint->location));
 
 					if (!notnull_constraint->conname && constraint->conname)
 						notnull_constraint->conname = constraint->conname;
@@ -1072,7 +1076,8 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
 			if (cxt->ispartitioned && constraint->is_no_inherit)
 				ereport(ERROR,
 						errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						errmsg("not-null constraints on partitioned tables cannot be NO INHERIT"));
+						errmsg("not-null constraints on partitioned tables cannot be NO INHERIT"),
+						parser_errposition(cxt->pstate, constraint->location));
 
 			cxt->nnconstraints = lappend(cxt->nnconstraints, constraint);
 			break;
@@ -1615,7 +1620,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
 
 	Assert(ofTypename);
 
-	tuple = typenameType(NULL, ofTypename, NULL);
+	tuple = typenameType(cxt->pstate, ofTypename, NULL);
 	check_of_type(tuple);
 	ofTypeId = ((Form_pg_type) GETSTRUCT(tuple))->oid;
 	ofTypename->typeOid = ofTypeId; /* cached for later */
@@ -3627,7 +3632,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 							ereport(ERROR,
 									(errcode(ERRCODE_UNDEFINED_COLUMN),
 									 errmsg("column \"%s\" of relation \"%s\" does not exist",
-											cmd->name, RelationGetRelationName(rel))));
+											cmd->name, RelationGetRelationName(rel)),
+									 parser_errposition(pstate, def->location)));
 
 						if (attnum > 0 &&
 							TupleDescAttr(tupdesc, attnum - 1)->attidentity)
@@ -3667,7 +3673,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 						ereport(ERROR,
 								(errcode(ERRCODE_UNDEFINED_COLUMN),
 								 errmsg("column \"%s\" of relation \"%s\" does not exist",
-										cmd->name, RelationGetRelationName(rel))));
+										cmd->name, RelationGetRelationName(rel)),
+								 parser_errposition(pstate, def->location)));
 
 					generateSerialExtraStmts(&cxt, newdef,
 											 get_atttype(relid, attnum),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f28bf37105..aa24283eff 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1712,7 +1712,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_CreateDomainStmt:
-				address = DefineDomain((CreateDomainStmt *) parsetree);
+				address = DefineDomain(pstate, (CreateDomainStmt *) parsetree);
 				break;
 
 			case T_CreateConversionStmt:
@@ -1801,7 +1801,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_AlterTypeStmt:
-				address = AlterType((AlterTypeStmt *) parsetree);
+				address = AlterType(pstate, (AlterTypeStmt *) parsetree);
 				break;
 
 			case T_CommentStmt:
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index e1b02927c4..6e493b896c 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -23,7 +23,7 @@
 
 extern ObjectAddress DefineType(ParseState *pstate, List *names, List *parameters);
 extern void RemoveTypeById(Oid typeOid);
-extern ObjectAddress DefineDomain(CreateDomainStmt *stmt);
+extern ObjectAddress DefineDomain(ParseState *pstate, CreateDomainStmt *stmt);
 extern ObjectAddress DefineEnum(CreateEnumStmt *stmt);
 extern ObjectAddress DefineRange(ParseState *pstate, CreateRangeStmt *stmt);
 extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
@@ -58,6 +58,6 @@ extern Oid	AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 									   bool errorOnTableType,
 									   ObjectAddresses *objsMoved);
 
-extern ObjectAddress AlterType(AlterTypeStmt *stmt);
+extern ObjectAddress AlterType(ParseState *pstate, AlterTypeStmt *stmt);
 
 #endif							/* TYPECMDS_H */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 17802f02e8..ff64a23a5f 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3114,6 +3114,8 @@ DROP TYPE test_type1;
 CREATE TYPE test_type2 AS (a int, b text);
 CREATE TABLE test_tbl2 OF xx;
 ERROR:  type "xx" does not exist
+LINE 1: CREATE TABLE test_tbl2 OF xx;
+                                  ^
 CREATE TABLE test_tbl2 OF test_type2;
 CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2);
 \d test_type2
@@ -3267,6 +3269,8 @@ CREATE TABLE tt7 (x int, q text, y numeric(8,2));
 ALTER TABLE tt7 DROP q;								-- OK
 ALTER TABLE tt0 OF tt_t_noexist;
 ERROR:  type "tt_t_noexist" does not exist
+LINE 1: ALTER TABLE tt0 OF tt_t_noexist;
+                           ^
 ALTER TABLE tt0 OF tt_t0;
 ALTER TABLE tt1 OF tt_t0;
 ERROR:  table "tt1" has different type for column "y"
@@ -3420,10 +3424,16 @@ ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
 --test error report position
 ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
 ERROR:  cannot alter system column "xmin"
+LINE 1: ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+                                              ^
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
 ERROR:  type "x" does not exist
+LINE 1: ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+                                                               ^
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
 ERROR:  collations are not supported by type integer
+LINE 1: ...LE comment_test ALTER COLUMN id SET DATA TYPE int collate "C...
+                                                             ^
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
            comment           
diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out
index 6cbadafcfb..d4f327636f 100644
--- a/src/test/regress/expected/collate.icu.utf8.out
+++ b/src/test/regress/expected/collate.icu.utf8.out
@@ -120,6 +120,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv-x-icu";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out
index 01664f7c1b..fbaab7cdf8 100644
--- a/src/test/regress/expected/collate.linux.utf8.out
+++ b/src/test/regress/expected/collate.linux.utf8.out
@@ -122,6 +122,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out
index 593a622637..bf72908fbd 100644
--- a/src/test/regress/expected/collate.out
+++ b/src/test/regress/expected/collate.out
@@ -73,6 +73,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "P...
 CREATE DOMAIN testdomain_p AS text COLLATE "POSIX";
 CREATE DOMAIN testdomain_i AS int COLLATE "POSIX"; -- fail
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "POSIX";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_p
diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out
index 31f794988b..4644f56b31 100644
--- a/src/test/regress/expected/collate.windows.win1252.out
+++ b/src/test/regress/expected/collate.windows.win1252.out
@@ -124,6 +124,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/constraints.out b/src/test/regress/expected/constraints.out
index 71200c90ed..ff3b710468 100644
--- a/src/test/regress/expected/constraints.out
+++ b/src/test/regress/expected/constraints.out
@@ -925,6 +925,8 @@ create table notnull_tbl_fail (a serial constraint foo not null constraint bar n
 ERROR:  conflicting not-null constraint names "foo" and "bar"
 create table notnull_tbl_fail (a serial constraint foo not null no inherit constraint foo not null);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: create table notnull_tbl_fail (a serial constraint foo not n...
+                                                ^
 create table notnull_tbl_fail (a int constraint foo not null, constraint foo not null a no inherit);
 ERROR:  conflicting NO INHERIT declaration for not-null constraint on column "a"
 create table notnull_tbl_fail (a serial constraint foo not null, constraint bar not null a);
@@ -935,12 +937,18 @@ create table notnull_tbl_fail (a serial, constraint foo not null a no inherit);
 ERROR:  conflicting NO INHERIT declaration for not-null constraint on column "a"
 create table notnull_tbl_fail (a serial not null no inherit);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: create table notnull_tbl_fail (a serial not null no inherit)...
+                                                ^
 create table notnull_tbl_fail (like notnull_tbl1, constraint foo2 not null a);
 ERROR:  conflicting not-null constraint names "foo" and "foo2"
 create table notnull_tbl_fail (a int primary key constraint foo not null no inherit);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: create table notnull_tbl_fail (a int primary key constraint ...
+                                                         ^
 create table notnull_tbl_fail (a int not null no inherit primary key);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: create table notnull_tbl_fail (a int not null no inherit pri...
+                                             ^
 create table notnull_tbl_fail (a int primary key, not null a no inherit);
 ERROR:  conflicting NO INHERIT declaration for not-null constraint on column "a"
 create table notnull_tbl_fail (a int, primary key(a), not null a no inherit);
@@ -949,6 +957,8 @@ create table notnull_tbl_fail (a int generated by default as identity, constrain
 ERROR:  conflicting NO INHERIT declaration for not-null constraint on column "a"
 create table notnull_tbl_fail (a int generated by default as identity not null no inherit);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: ..._tbl_fail (a int generated by default as identity not null n...
+                                                             ^
 drop table notnull_tbl1;
 -- NOT NULL NO INHERIT
 CREATE TABLE ATACC1 (a int, NOT NULL a NO INHERIT);
@@ -998,8 +1008,12 @@ DROP TABLE ATACC1, ATACC2, ATACC3;
 -- NOT NULL NO INHERIT is not possible on partitioned tables
 CREATE TABLE ATACC1 (a int NOT NULL NO INHERIT) PARTITION BY LIST (a);
 ERROR:  not-null constraints on partitioned tables cannot be NO INHERIT
+LINE 1: CREATE TABLE ATACC1 (a int NOT NULL NO INHERIT) PARTITION BY...
+                                   ^
 CREATE TABLE ATACC1 (a int, NOT NULL a NO INHERIT) PARTITION BY LIST (a);
 ERROR:  not-null constraints on partitioned tables cannot be NO INHERIT
+LINE 1: CREATE TABLE ATACC1 (a int, NOT NULL a NO INHERIT) PARTITION...
+                                    ^
 -- it's not possible to override a no-inherit constraint with an inheritable one
 CREATE TABLE ATACC2 (a int, CONSTRAINT a_is_not_null NOT NULL a NO INHERIT);
 CREATE TABLE ATACC1 (a int);
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index c5cc41cacd..222bb54c75 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -19,40 +19,76 @@ ERROR:  type "domaindroptest" does not exist
 -- create domain error case.
 create domain d_fail as x;
 ERROR:  type "x" does not exist
+LINE 1: create domain d_fail as x;
+                                ^
 create domain d_fail as int constraint cc REFERENCES this_table_not_exists(i);
 ERROR:  foreign key constraints not possible for domains
+LINE 1: create domain d_fail as int constraint cc REFERENCES this_ta...
+                                    ^
 create domain d_fail as int4 not null no inherit;
 ERROR:  not-null constraints for domains cannot be marked NO INHERIT
+LINE 1: create domain d_fail as int4 not null no inherit;
+                                     ^
 create domain d_fail as int4 not null null;
 ERROR:  conflicting NULL/NOT NULL constraints
+LINE 1: create domain d_fail as int4 not null null;
+                                              ^
 create domain d_fail as int4 not null default 3 default 3;
 ERROR:  multiple default expressions
+LINE 1: create domain d_fail as int4 not null default 3 default 3;
+                                                        ^
 create domain d_fail int4 DEFAULT 3 + 'h';
 ERROR:  invalid input syntax for type integer: "h"
+LINE 1: create domain d_fail int4 DEFAULT 3 + 'h';
+                                              ^
 create domain d_fail int4 collate "C";
 ERROR:  collations are not supported by type integer
+LINE 1: create domain d_fail int4 collate "C";
+                             ^
 create domain d_fail as anyelement;
 ERROR:  "anyelement" is not a valid base type for a domain
+LINE 1: create domain d_fail as anyelement;
+                                ^
 create domain d_fail as int4 unique;
 ERROR:  unique constraints not possible for domains
+LINE 1: create domain d_fail as int4 unique;
+                                     ^
 create domain d_fail as int4 PRIMARY key;
 ERROR:  primary key constraints not possible for domains
+LINE 1: create domain d_fail as int4 PRIMARY key;
+                                     ^
 create domain d_fail as int4 constraint cc generated by default as identity;
 ERROR:  specifying GENERATED not supported for domains
+LINE 1: create domain d_fail as int4 constraint cc generated by defa...
+                                     ^
 create domain d_fail as int4 constraint cc generated always as (2) stored;
 ERROR:  specifying GENERATED not supported for domains
+LINE 1: create domain d_fail as int4 constraint cc generated always ...
+                                     ^
 create domain d_fail as int4 constraint cc check(values > 1) no inherit;
 ERROR:  check constraints for domains cannot be marked NO INHERIT
+LINE 1: create domain d_fail as int4 constraint cc check(values > 1)...
+                                     ^
 create domain d_fail as int4 constraint cc check(values > 1) deferrable;
 ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) deferrable...
+                                                             ^
 create domain d_fail as int4 constraint cc check(values > 1) not deferrable;
 ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) not deferr...
+                                                             ^
 create domain d_fail as int4 constraint cc check (value > 1) initially deferred;
 ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check (value > 1) initially ...
+                                                             ^
 create domain d_fail as int4 constraint cc check(values > 1) initially immediate;
 ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) initially ...
+                                                             ^
 create domain d_fail as int4 constraint cc check(values > 1) deferrable not deferrable ;
 ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...in d_fail as int4 constraint cc check(values > 1) deferrable...
+                                                             ^
 -- Test domain input.
 -- Note: the point of checking both INSERT and COPY FROM is that INSERT
 -- exercises CoerceToDomain while COPY exercises domain_in.
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index 1befc47185..3e49b5a18e 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -1027,6 +1027,8 @@ LINE 1: create function xfloat8out(xfloat8) returns cstring immutabl...
 --should fail, since type x does not exists.
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
 ERROR:  type "x" does not exist
+LINE 1: ... xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
+                                                                    ^
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 0398a19484..0b370235ea 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -45,6 +45,8 @@ ERROR:  column "a" of relation "itest4" must be declared NOT NULL before identit
 ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
 ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS IDENTITY;  -- error, column c does not exist
 ERROR:  column "c" of relation "itest4" does not exist
+LINE 1: ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS ID...
+                                              ^
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- ok
 ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL;  -- error, disallowed
 ERROR:  column "a" of relation "itest4" is an identity column
diff --git a/src/test/regress/expected/typed_table.out b/src/test/regress/expected/typed_table.out
index b6fbda3f21..885f085e15 100644
--- a/src/test/regress/expected/typed_table.out
+++ b/src/test/regress/expected/typed_table.out
@@ -1,5 +1,7 @@
 CREATE TABLE ttable1 OF nothing;
 ERROR:  type "nothing" does not exist
+LINE 1: CREATE TABLE ttable1 OF nothing;
+                                ^
 CREATE TYPE person_type AS (id int, name text);
 CREATE TABLE persons OF person_type;
 CREATE TABLE IF NOT EXISTS persons OF person_type;
@@ -36,6 +38,8 @@ ALTER TABLE persons RENAME COLUMN id TO num;
 ERROR:  cannot rename column of typed table
 ALTER TABLE persons ALTER COLUMN name TYPE varchar;
 ERROR:  cannot alter column type of typed table
+LINE 1: ALTER TABLE persons ALTER COLUMN name TYPE varchar;
+                                         ^
 CREATE TABLE stuff (id int);
 ALTER TABLE persons INHERIT stuff;
 ERROR:  cannot change inheritance of typed table
-- 
2.34.1

v9-0001-Add-more-regression-tests-to-various-DDL-patterns.patchtext/x-patch; charset=UTF-8; name=v9-0001-Add-more-regression-tests-to-various-DDL-patterns.patchDownload
From 40754ca1c6d4cde17f7ae0f77500c9cd04d5107e Mon Sep 17 00:00:00 2001
From: reshke kirill <reshke@double.cloud>
Date: Tue, 10 Dec 2024 08:39:50 +0000
Subject: [PATCH v9 1/2] Add more regression tests to various DDL patterns
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The main goal of this patch is to increase test coverage of CREATE
DOMAIN, ALTER TABLE ... etc DDL commands.

This is prelimitary patch for enhansing error position reporting feature.

Author: Jian He <jian.universality@gmail.com>
Author: Kirill Reshke <reshkekirill@gmail.com>
Reviewed-By: Michaël Paquier <michael@paquier.xyz>
Reviewed-By: Álvaro Herrera <alvherre@alvh.no-ip.org>

discussion: https://postgr.es/m/CALdSSPhqfvKbDwqJaY=yEePi_aq61GmMpW88i6ZH7CMG_2Z4Cg@mail.gmail.com
---
 src/test/regress/expected/alter_table.out | 11 +++++++
 src/test/regress/expected/domain.out      | 38 +++++++++++++++++++++++
 src/test/regress/expected/float8.out      |  3 ++
 src/test/regress/expected/identity.out    |  2 ++
 src/test/regress/sql/alter_table.sql      |  7 +++++
 src/test/regress/sql/domain.sql           | 21 +++++++++++++
 src/test/regress/sql/float8.sql           |  2 ++
 src/test/regress/sql/identity.sql         |  1 +
 8 files changed, 85 insertions(+)

diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 2212c8dbb5..17802f02e8 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3112,6 +3112,8 @@ ERROR:  cannot alter type "test_type1" because column "test_tbl1_idx.row" uses i
 DROP TABLE test_tbl1;
 DROP TYPE test_type1;
 CREATE TYPE test_type2 AS (a int, b text);
+CREATE TABLE test_tbl2 OF xx;
+ERROR:  type "xx" does not exist
 CREATE TABLE test_tbl2 OF test_type2;
 CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2);
 \d test_type2
@@ -3263,6 +3265,8 @@ CREATE TABLE tt5 (x int, y numeric(8,2), z int);	-- too few columns
 CREATE TABLE tt6 () INHERITS (tt0);					-- can't have a parent
 CREATE TABLE tt7 (x int, q text, y numeric(8,2));
 ALTER TABLE tt7 DROP q;								-- OK
+ALTER TABLE tt0 OF tt_t_noexist;
+ERROR:  type "tt_t_noexist" does not exist
 ALTER TABLE tt0 OF tt_t0;
 ALTER TABLE tt1 OF tt_t0;
 ERROR:  table "tt1" has different type for column "y"
@@ -3413,6 +3417,13 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ERROR:  cannot alter system column "xmin"
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ERROR:  type "x" does not exist
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+ERROR:  collations are not supported by type integer
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
            comment           
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 42b6559f9c..c5cc41cacd 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -15,6 +15,44 @@ NOTICE:  drop cascades to type dependenttypetest
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 ERROR:  type "domaindroptest" does not exist
+-- Test syntax.
+-- create domain error case.
+create domain d_fail as x;
+ERROR:  type "x" does not exist
+create domain d_fail as int constraint cc REFERENCES this_table_not_exists(i);
+ERROR:  foreign key constraints not possible for domains
+create domain d_fail as int4 not null no inherit;
+ERROR:  not-null constraints for domains cannot be marked NO INHERIT
+create domain d_fail as int4 not null null;
+ERROR:  conflicting NULL/NOT NULL constraints
+create domain d_fail as int4 not null default 3 default 3;
+ERROR:  multiple default expressions
+create domain d_fail int4 DEFAULT 3 + 'h';
+ERROR:  invalid input syntax for type integer: "h"
+create domain d_fail int4 collate "C";
+ERROR:  collations are not supported by type integer
+create domain d_fail as anyelement;
+ERROR:  "anyelement" is not a valid base type for a domain
+create domain d_fail as int4 unique;
+ERROR:  unique constraints not possible for domains
+create domain d_fail as int4 PRIMARY key;
+ERROR:  primary key constraints not possible for domains
+create domain d_fail as int4 constraint cc generated by default as identity;
+ERROR:  specifying GENERATED not supported for domains
+create domain d_fail as int4 constraint cc generated always as (2) stored;
+ERROR:  specifying GENERATED not supported for domains
+create domain d_fail as int4 constraint cc check(values > 1) no inherit;
+ERROR:  check constraints for domains cannot be marked NO INHERIT
+create domain d_fail as int4 constraint cc check(values > 1) deferrable;
+ERROR:  specifying constraint deferrability not supported for domains
+create domain d_fail as int4 constraint cc check(values > 1) not deferrable;
+ERROR:  specifying constraint deferrability not supported for domains
+create domain d_fail as int4 constraint cc check (value > 1) initially deferred;
+ERROR:  specifying constraint deferrability not supported for domains
+create domain d_fail as int4 constraint cc check(values > 1) initially immediate;
+ERROR:  specifying constraint deferrability not supported for domains
+create domain d_fail as int4 constraint cc check(values > 1) deferrable not deferrable ;
+ERROR:  specifying constraint deferrability not supported for domains
 -- Test domain input.
 -- Note: the point of checking both INSERT and COPY FROM is that INSERT
 -- exercises CoerceToDomain while COPY exercises domain_in.
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index de56998f5c..1befc47185 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -1024,6 +1024,9 @@ create function xfloat8out(xfloat8) returns cstring immutable strict
 NOTICE:  argument type xfloat8 is only a shell
 LINE 1: create function xfloat8out(xfloat8) returns cstring immutabl...
                                    ^
+--should fail, since type x does not exists.
+create type xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
+ERROR:  type "x" does not exist
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 2a2b777c89..0398a19484 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -43,6 +43,8 @@ CREATE TABLE itest4 (a int, b text);
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, requires NOT NULL
 ERROR:  column "a" of relation "itest4" must be declared NOT NULL before identity can be added
 ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
+ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS IDENTITY;  -- error, column c does not exist
+ERROR:  column "c" of relation "itest4" does not exist
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- ok
 ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL;  -- error, disallowed
 ERROR:  column "a" of relation "itest4" is an identity column
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 637e3dac38..67c2f89a47 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1985,6 +1985,7 @@ DROP TABLE test_tbl1;
 DROP TYPE test_type1;
 
 CREATE TYPE test_type2 AS (a int, b text);
+CREATE TABLE test_tbl2 OF xx;
 CREATE TABLE test_tbl2 OF test_type2;
 CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2);
 \d test_type2
@@ -2048,6 +2049,7 @@ CREATE TABLE tt6 () INHERITS (tt0);					-- can't have a parent
 CREATE TABLE tt7 (x int, q text, y numeric(8,2));
 ALTER TABLE tt7 DROP q;								-- OK
 
+ALTER TABLE tt0 OF tt_t_noexist;
 ALTER TABLE tt0 OF tt_t0;
 ALTER TABLE tt1 OF tt_t0;
 ALTER TABLE tt2 OF tt_t0;
@@ -2145,6 +2147,11 @@ ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
 ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
 
+--test error report position
+ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int collate "C";
+
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
 SELECT indexrelid::regclass::text as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass ORDER BY 1, 2;
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index ee07b03174..5faa56b31e 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -16,6 +16,27 @@ drop domain domaindroptest cascade;
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 
+-- Test syntax.
+-- create domain error case.
+create domain d_fail as x;
+create domain d_fail as int constraint cc REFERENCES this_table_not_exists(i);
+create domain d_fail as int4 not null no inherit;
+create domain d_fail as int4 not null null;
+create domain d_fail as int4 not null default 3 default 3;
+create domain d_fail int4 DEFAULT 3 + 'h';
+create domain d_fail int4 collate "C";
+create domain d_fail as anyelement;
+
+create domain d_fail as int4 unique;
+create domain d_fail as int4 PRIMARY key;
+create domain d_fail as int4 constraint cc generated by default as identity;
+create domain d_fail as int4 constraint cc generated always as (2) stored;
+create domain d_fail as int4 constraint cc check(values > 1) no inherit;
+create domain d_fail as int4 constraint cc check(values > 1) deferrable;
+create domain d_fail as int4 constraint cc check(values > 1) not deferrable;
+create domain d_fail as int4 constraint cc check (value > 1) initially deferred;
+create domain d_fail as int4 constraint cc check(values > 1) initially immediate;
+create domain d_fail as int4 constraint cc check(values > 1) deferrable not deferrable ;
 
 -- Test domain input.
 
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index 98e9926c9e..d78cbf5f60 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -328,6 +328,8 @@ create function xfloat8in(cstring) returns xfloat8 immutable strict
   language internal as 'int8in';
 create function xfloat8out(xfloat8) returns cstring immutable strict
   language internal as 'int8out';
+--should fail, since type x does not exists.
+create type xfloat8 (input = xfloat8in, output = xfloat8out, like = x);
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index cb0e05a2f1..45992a3d89 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -19,6 +19,7 @@ SELECT pg_get_serial_sequence('itest1', 'a');
 CREATE TABLE itest4 (a int, b text);
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, requires NOT NULL
 ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
+ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS IDENTITY;  -- error, column c does not exist
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- ok
 ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL;  -- error, disallowed
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, already set
-- 
2.34.1

#13Tom Lane
tgl@sss.pgh.pa.us
In reply to: jian he (#12)
Re: Pass ParseState as down to utility functions.

jian he <jian.universality@gmail.com> writes:

add parser_errposition to some places in
transformTableConstraint, transformColumnDefinition
where v8 didn't.

I'm not loving the idea of cons'ing up ParseStates in random places in
tablecmds.c. I think we ought to fix things so that the one made in
standard_ProcessUtility is passed down to all these places, replacing
ad-hoc queryString and queryEnv parameters.

Eventually we might want to make ProcessUtility's callers pass down
a pstate, but that would be a whole other area of invasive changes,
so I think we should leave that idea for later. Right now though,
it seems reasonable to change AlterTableUtilityContext to replace
the queryString and queryEnv fields with a ParseState carrying that
info --- and, perhaps, someday saving us from adding more ad-hoc
fields there.

regards, tom lane

#14jian he
jian.universality@gmail.com
In reply to: Tom Lane (#13)
Re: Pass ParseState as down to utility functions.

On Thu, Dec 12, 2024 at 4:48 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

jian he <jian.universality@gmail.com> writes:

add parser_errposition to some places in
transformTableConstraint, transformColumnDefinition
where v8 didn't.

I'm not loving the idea of cons'ing up ParseStates in random places in
tablecmds.c. I think we ought to fix things so that the one made in
standard_ProcessUtility is passed down to all these places, replacing
ad-hoc queryString and queryEnv parameters.

the main code change is within DefineDomain.

AlterTableUtilityContext comments says:
/* Info needed when recursing from ALTER TABLE */
so we cannot pass DefineDomain with AlterTableUtilityContext.

-DefineDomain(CreateDomainStmt *stmt)
+DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
we have to pass either ParseState or queryString to DefineDomain.
-extern ObjectAddress AlterType(AlterTypeStmt *stmt);
+extern ObjectAddress AlterType(ParseState *pstate, AlterTypeStmt *stmt);
this change not necessary, we can remove it.

but other places (listed in below),
we are passing (AlterTableUtilityContext *context) which seems ok?

-ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
+ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode,
+ AlterTableUtilityContext *context)
 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-   AlterTableCmd *cmd, LOCKMODE lockmode);
+   AlterTableCmd *cmd, LOCKMODE lockmode,
+   AlterTableUtilityContext *context);
 static ObjectAddress
 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-  AlterTableCmd *cmd, LOCKMODE lockmode)
+  AlterTableCmd *cmd, LOCKMODE lockmode,
+  AlterTableUtilityContext *context)
#15Michael Paquier
michael@paquier.xyz
In reply to: jian he (#12)
Re: Pass ParseState as down to utility functions.

On Tue, Dec 10, 2024 at 10:38:41PM +0800, jian he wrote:

add parser_errposition to some places in
transformTableConstraint, transformColumnDefinition
where v8 didn't.

I've looked at the new tests in 0001. Here are some notes. And I've
found some mistakes and simplifications on the way.

 CREATE TYPE test_type2 AS (a int, b text);
+CREATE TABLE test_tbl2 OF xx;
+ERROR:  type "xx" does not exist
[...]
+ALTER TABLE tt0 OF tt_t_noexist;
+ERROR:  type "tt_t_noexist" does not exist

typed_table.out checks that already, so these additions bring nothing
new:
CREATE TABLE ttable1 OF nothing;
ERROR: type "nothing" does not exist

The three tests for ALTER TABLE .. SET DATA TYPE are new patterns, so
these are OK. The COLLATE case was kind of covered with CREATE
DOMAIN, but the command is different.

The ALTER TABLE .. ALTER COLUMN case for a generated column is new, so
that's OK.

CREATE TYPE (like=no_such_type) also makes sense, that's new coverage.
This query pattern is only used in expressions, float8 and float4.

+--test error report position

As of 0001, this comment is incorrect. We're not testing an error
position yet. With 0002, it would be correct. Let's just use a more
generic wording that applies to both patches.

+create domain d_fail as int4 constraint cc generated always as (2) stored;
+ERROR:  specifying GENERATED not supported for domains
+create domain d_fail as int4 constraint cc check(values > 1) no inherit;
+ERROR:  check constraints for domains cannot be marked NO INHERIT

Can be reduced to one rather than two.

+create domain d_fail as int4 constraint cc check(values > 1) deferrable;
+ERROR:  specifying constraint deferrability not supported for domains
+create domain d_fail as int4 constraint cc check(values > 1) not deferrable;
+ERROR:  specifying constraint deferrability not supported for domains
+create domain d_fail as int4 constraint cc check (value > 1) initially deferred;
+ERROR:  specifying constraint deferrability not supported for domains
+create domain d_fail as int4 constraint cc check(values > 1) initially immediate;
+ERROR:  specifying constraint deferrability not supported for domains
+create domain d_fail as int4 constraint cc check(values > 1) deferrable not deferrable ;
+ERROR:  specifying constraint deferrability not supported for domains

Testing the full set of keywords is not really interesting. So let's
just use one to make the script cheaper.

I was wondering for a few seconds about exclusion constraints, but it
requires a named constraint to trigger the error, so I've left it out
for now.

+create domain d_fail as int constraint cc REFERENCES this_table_not_exists(i);

Hmm. Funny. We don't document REFERENCES in the docs of CREATE
DOMAIN. Neither do we document GENERATED. Looks like a doc issue to
me, independent of this thread. ALTER DOMAIN uses a different parsing
clause than CREATE DOMAIN, meaning that generated columns or
references cannot be altered.. It looks like there's quite a bit more
going on here. The fact that we don't have tests for these patterns
authorized by the parser should be tracked anyway, so let's add them
for now. This should be looked at on a separate thread.

For now, I've applied the new tests. Let's move on with the additions
of 0002, and see if these are good to have or not (noticed Tom's
comments about the type paths, of course).
--
Michael

#16Michael Paquier
michael@paquier.xyz
In reply to: jian he (#14)
Re: Pass ParseState as down to utility functions.

On Thu, Dec 12, 2024 at 10:08:04AM +0800, jian he wrote:

On Thu, Dec 12, 2024 at 4:48 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

I'm not loving the idea of cons'ing up ParseStates in random places in
tablecmds.c. I think we ought to fix things so that the one made in
standard_ProcessUtility is passed down to all these places, replacing
ad-hoc queryString and queryEnv parameters.

the main code change is within DefineDomain.

[ .. more comments from Jian .. ]

Yeah, I'm not much a fan of some of the changes of tablecmds.c, that
makes the whole stack more complicated than it should.

ProcessUtilitySlow() passing down a ParseState to DefineDomain() and
AlterType() is more consistent and a good idea IMO.

I think that the patch should be split a bit more. Even if different
areas of the code are touched, there's more than one idea of how to
create this information for the error position, so each piece could be
committed separately with changes showing up as diffs in the
regression tests.
--
Michael

#17jian he
jian.universality@gmail.com
In reply to: Michael Paquier (#16)
2 attachment(s)
Re: Pass ParseState as down to utility functions.

On Thu, Dec 12, 2024 at 10:29 AM Michael Paquier <michael@paquier.xyz> wrote:

I think that the patch should be split a bit more. Even if different
areas of the code are touched, there's more than one idea of how to
create this information for the error position, so each piece could be
committed separately with changes showing up as diffs in the
regression tests.
--

I've split it into two patches, one for CREATE DOMAIN only. one for
another DDL command.

0001:
I am using DefineDomain(ParseState *pstate, CreateDomainStmt *stmt) for now.
we can also pass querystring or another struct.

0002:
passing AlterTableUtilityContext for some ALTER TABLE subroutine.
add parser_errposition to some existing `ereport(ERROR` places.
-------------------
you mentioned ALTER DOMAIN, I have further simplified it at
/messages/by-id/CACJufxG0N_WLfk-NC_k5w6vv26qLvXupbHvnkKtc2npftJQicQ@mail.gmail.com

create domain d_fail as int constraint cc REFERENCES this_table_not_exists(i);
like this command will fail, so we don't need to change
create_domain.sgml synopsis section
?

Attachments:

v11-0002-print-out-the-error-position-for-some-DDL-comman.patchtext/x-patch; charset=UTF-8; name=v11-0002-print-out-the-error-position-for-some-DDL-comman.patchDownload
From 2f28cc9b4f605b981801a307e44de6cbc9f504e5 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Thu, 12 Dec 2024 12:15:16 +0800
Subject: [PATCH v11 2/2] print out the error position for some DDL commands.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This can be particularly helpful when working with a sequence of DML commands,
such as `create schema create schema_element`.
It also makes it easier to quickly identify the relevant error area in a single DDL command

these DDL commands will printout error position:
ALTER TABLE ALTER COLUMN SET DATA TYPE;
CREATE TABLE OF type;
ALTER TABLE ALTER COLUMN ADD GENERATED ALWAYS AS IDENTITY;
CREATE TYPE LIKE type.
CREATE TABLE while specifying conflict constraint.

Author: Kirill Reshke <reshkekirill@gmail.com>
Author: Jian He <jian.universality@gmail.com>
Reviewed-By: Michaël Paquier <michael@paquier.xyz>
Reviewed-By: Álvaro Herrera <alvherre@alvh.no-ip.org>

discussion: https://postgr.es/m/CALdSSPhqfvKbDwqJaY=yEePi_aq61GmMpW88i6ZH7CMG_2Z4Cg@mail.gmail.com
---
 src/backend/commands/tablecmds.c          | 43 +++++++++++++++--------
 src/backend/commands/typecmds.c           |  2 +-
 src/backend/parser/parse_utilcmd.c        | 21 +++++++----
 src/test/regress/expected/alter_table.out |  6 ++++
 src/test/regress/expected/constraints.out | 14 ++++++++
 src/test/regress/expected/float8.out      |  2 ++
 src/test/regress/expected/identity.out    |  2 ++
 src/test/regress/expected/typed_table.out |  4 +++
 8 files changed, 72 insertions(+), 22 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6ccae4cb4a..efa38b1470 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -593,7 +593,8 @@ static void ATPrepAlterColumnType(List **wqueue,
 								  AlterTableUtilityContext *context);
 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-										   AlterTableCmd *cmd, LOCKMODE lockmode);
+										   AlterTableCmd *cmd, LOCKMODE lockmode,
+										   AlterTableUtilityContext *context);
 static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
 											  Relation rel, AttrNumber attnum, const char *colName);
 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
@@ -639,7 +640,9 @@ static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCK
 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
 								   DependencyType deptype);
-static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
+static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename,
+								 LOCKMODE lockmode,
+								 AlterTableUtilityContext *context);
 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
 static void ATExecGenericOptions(Relation rel, List *options);
@@ -5413,7 +5416,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			break;
 		case AT_AlterColumnType:	/* ALTER COLUMN TYPE */
 			/* parse transformation was done earlier */
-			address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
+			address = ATExecAlterColumnType(tab, rel, cmd, lockmode, context);
 			break;
 		case AT_AlterColumnGenericOptions:	/* ALTER COLUMN OPTIONS */
 			address =
@@ -5537,7 +5540,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 			address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
 			break;
 		case AT_AddOf:
-			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
+			address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode, context);
 			break;
 		case AT_DropOf:
 			ATExecDropOf(rel, lockmode);
@@ -13218,10 +13221,12 @@ ATPrepAlterColumnType(List **wqueue,
 	AclResult	aclresult;
 	bool		is_expr;
 
+	pstate->p_sourcetext = context->queryString;
 	if (rel->rd_rel->reloftype && !recursing)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("cannot alter column type of typed table")));
+				 errmsg("cannot alter column type of typed table"),
+				 parser_errposition(pstate, def->location)));
 
 	/* lookup the attribute so we can check inheritance status */
 	tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
@@ -13237,8 +13242,8 @@ ATPrepAlterColumnType(List **wqueue,
 	if (attnum <= 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot alter system column \"%s\"",
-						colName)));
+				 errmsg("cannot alter system column \"%s\"",colName),
+				 parser_errposition(pstate, def->location)));
 
 	/*
 	 * Cannot specify USING when altering type of a generated column, because
@@ -13271,14 +13276,14 @@ ATPrepAlterColumnType(List **wqueue,
 						colName, RelationGetRelationName(rel))));
 
 	/* Look up the target type */
-	typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
+	typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
 
 	aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error_type(aclresult, targettype);
 
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colName, targettype, targetcollid,
@@ -13537,7 +13542,8 @@ ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
  */
 static ObjectAddress
 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-					  AlterTableCmd *cmd, LOCKMODE lockmode)
+					  AlterTableCmd *cmd, LOCKMODE lockmode,
+					  AlterTableUtilityContext *context)
 {
 	char	   *colName = cmd->name;
 	ColumnDef  *def = (ColumnDef *) cmd->def;
@@ -13558,6 +13564,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	SysScanDesc scan;
 	HeapTuple	depTup;
 	ObjectAddress address;
+	ParseState      *pstate;
+
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
 
 	/*
 	 * Clear all the missing values if we're rewriting the table, since this
@@ -13596,11 +13606,11 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 						colName)));
 
 	/* Look up the target type (should not fail, since prep found it) */
-	typeTuple = typenameType(NULL, typeName, &targettypmod);
+	typeTuple = typenameType(pstate, typeName, &targettypmod);
 	tform = (Form_pg_type) GETSTRUCT(typeTuple);
 	targettype = tform->oid;
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/*
 	 * If there is a default expression for the column, get it and ensure we
@@ -16976,7 +16986,8 @@ drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
  * The address of the type is returned.
  */
 static ObjectAddress
-ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
+ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode,
+			AlterTableUtilityContext *context)
 {
 	Oid			relid = RelationGetRelid(rel);
 	Type		typetuple;
@@ -16993,9 +17004,13 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 	ObjectAddress tableobj,
 				typeobj;
 	HeapTuple	classtuple;
+	ParseState 	*pstate;
 
 	/* Validate the type. */
-	typetuple = typenameType(NULL, ofTypename, NULL);
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = context->queryString;
+
+	typetuple = typenameType(pstate, ofTypename, NULL);
 	check_of_type(typetuple);
 	typeform = (Form_pg_type) GETSTRUCT(typetuple);
 	typeid = typeform->oid;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 6127313956..4f20b5be06 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -348,7 +348,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 		Type		likeType;
 		Form_pg_type likeForm;
 
-		likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+		likeType = typenameType(pstate, defGetTypeName(likeTypeEl), NULL);
 		likeForm = (Form_pg_type) GETSTRUCT(likeType);
 		internalLength = likeForm->typlen;
 		byValue = likeForm->typbyval;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0f324ee4e3..7223911bf1 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -756,7 +756,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 				if (cxt->ispartitioned && constraint->is_no_inherit)
 					ereport(ERROR,
 							errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-							errmsg("not-null constraints on partitioned tables cannot be NO INHERIT"));
+							errmsg("not-null constraints on partitioned tables cannot be NO INHERIT"),
+							parser_errposition(cxt->pstate, constraint->location));
 
 				/* Disallow conflicting [NOT] NULL markings */
 				if (saw_nullable && !column->is_not_null)
@@ -771,7 +772,9 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 					ereport(ERROR,
 							errcode(ERRCODE_SYNTAX_ERROR),
 							errmsg("conflicting NO INHERIT declarations for not-null constraints on column \"%s\"",
-								   column->colname));
+								   column->colname),
+							parser_errposition(cxt->pstate, constraint->location));
+
 
 				/*
 				 * If this is the first time we see this column being marked
@@ -806,7 +809,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 						ereport(ERROR,
 								errcode(ERRCODE_SYNTAX_ERROR),
 								errmsg("conflicting NO INHERIT declarations for not-null constraints on column \"%s\"",
-									   column->colname));
+									   column->colname),
+								parser_errposition(cxt->pstate, constraint->location));
 
 					if (!notnull_constraint->conname && constraint->conname)
 						notnull_constraint->conname = constraint->conname;
@@ -1072,7 +1076,8 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
 			if (cxt->ispartitioned && constraint->is_no_inherit)
 				ereport(ERROR,
 						errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						errmsg("not-null constraints on partitioned tables cannot be NO INHERIT"));
+						errmsg("not-null constraints on partitioned tables cannot be NO INHERIT"),
+						parser_errposition(cxt->pstate, constraint->location));
 
 			cxt->nnconstraints = lappend(cxt->nnconstraints, constraint);
 			break;
@@ -1615,7 +1620,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
 
 	Assert(ofTypename);
 
-	tuple = typenameType(NULL, ofTypename, NULL);
+	tuple = typenameType(cxt->pstate, ofTypename, NULL);
 	check_of_type(tuple);
 	ofTypeId = ((Form_pg_type) GETSTRUCT(tuple))->oid;
 	ofTypename->typeOid = ofTypeId; /* cached for later */
@@ -3627,7 +3632,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 							ereport(ERROR,
 									(errcode(ERRCODE_UNDEFINED_COLUMN),
 									 errmsg("column \"%s\" of relation \"%s\" does not exist",
-											cmd->name, RelationGetRelationName(rel))));
+											cmd->name, RelationGetRelationName(rel)),
+									 parser_errposition(pstate, def->location)));
 
 						if (attnum > 0 &&
 							TupleDescAttr(tupdesc, attnum - 1)->attidentity)
@@ -3667,7 +3673,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 						ereport(ERROR,
 								(errcode(ERRCODE_UNDEFINED_COLUMN),
 								 errmsg("column \"%s\" of relation \"%s\" does not exist",
-										cmd->name, RelationGetRelationName(rel))));
+										cmd->name, RelationGetRelationName(rel)),
+								 parser_errposition(pstate, def->location)));
 
 					generateSerialExtraStmts(&cxt, newdef,
 											 get_atttype(relid, attnum),
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 12852aa612..57263f0709 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3416,10 +3416,16 @@ ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
 -- Some error cases.
 ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
 ERROR:  cannot alter system column "xmin"
+LINE 1: ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+                                              ^
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
 ERROR:  type "x" does not exist
+LINE 1: ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+                                                               ^
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int COLLATE "C";
 ERROR:  collations are not supported by type integer
+LINE 1: ...LE comment_test ALTER COLUMN id SET DATA TYPE int COLLATE "C...
+                                                             ^
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
            comment           
diff --git a/src/test/regress/expected/constraints.out b/src/test/regress/expected/constraints.out
index 71200c90ed..ff3b710468 100644
--- a/src/test/regress/expected/constraints.out
+++ b/src/test/regress/expected/constraints.out
@@ -925,6 +925,8 @@ create table notnull_tbl_fail (a serial constraint foo not null constraint bar n
 ERROR:  conflicting not-null constraint names "foo" and "bar"
 create table notnull_tbl_fail (a serial constraint foo not null no inherit constraint foo not null);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: create table notnull_tbl_fail (a serial constraint foo not n...
+                                                ^
 create table notnull_tbl_fail (a int constraint foo not null, constraint foo not null a no inherit);
 ERROR:  conflicting NO INHERIT declaration for not-null constraint on column "a"
 create table notnull_tbl_fail (a serial constraint foo not null, constraint bar not null a);
@@ -935,12 +937,18 @@ create table notnull_tbl_fail (a serial, constraint foo not null a no inherit);
 ERROR:  conflicting NO INHERIT declaration for not-null constraint on column "a"
 create table notnull_tbl_fail (a serial not null no inherit);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: create table notnull_tbl_fail (a serial not null no inherit)...
+                                                ^
 create table notnull_tbl_fail (like notnull_tbl1, constraint foo2 not null a);
 ERROR:  conflicting not-null constraint names "foo" and "foo2"
 create table notnull_tbl_fail (a int primary key constraint foo not null no inherit);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: create table notnull_tbl_fail (a int primary key constraint ...
+                                                         ^
 create table notnull_tbl_fail (a int not null no inherit primary key);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: create table notnull_tbl_fail (a int not null no inherit pri...
+                                             ^
 create table notnull_tbl_fail (a int primary key, not null a no inherit);
 ERROR:  conflicting NO INHERIT declaration for not-null constraint on column "a"
 create table notnull_tbl_fail (a int, primary key(a), not null a no inherit);
@@ -949,6 +957,8 @@ create table notnull_tbl_fail (a int generated by default as identity, constrain
 ERROR:  conflicting NO INHERIT declaration for not-null constraint on column "a"
 create table notnull_tbl_fail (a int generated by default as identity not null no inherit);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: ..._tbl_fail (a int generated by default as identity not null n...
+                                                             ^
 drop table notnull_tbl1;
 -- NOT NULL NO INHERIT
 CREATE TABLE ATACC1 (a int, NOT NULL a NO INHERIT);
@@ -998,8 +1008,12 @@ DROP TABLE ATACC1, ATACC2, ATACC3;
 -- NOT NULL NO INHERIT is not possible on partitioned tables
 CREATE TABLE ATACC1 (a int NOT NULL NO INHERIT) PARTITION BY LIST (a);
 ERROR:  not-null constraints on partitioned tables cannot be NO INHERIT
+LINE 1: CREATE TABLE ATACC1 (a int NOT NULL NO INHERIT) PARTITION BY...
+                                   ^
 CREATE TABLE ATACC1 (a int, NOT NULL a NO INHERIT) PARTITION BY LIST (a);
 ERROR:  not-null constraints on partitioned tables cannot be NO INHERIT
+LINE 1: CREATE TABLE ATACC1 (a int, NOT NULL a NO INHERIT) PARTITION...
+                                    ^
 -- it's not possible to override a no-inherit constraint with an inheritable one
 CREATE TABLE ATACC2 (a int, CONSTRAINT a_is_not_null NOT NULL a NO INHERIT);
 CREATE TABLE ATACC1 (a int);
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index 4965ee5554..9ef9793fe9 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -1026,6 +1026,8 @@ LINE 1: create function xfloat8out(xfloat8) returns cstring immutabl...
                                    ^
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = no_such_type);
 ERROR:  type "no_such_type" does not exist
+LINE 1: ...8 (input = xfloat8in, output = xfloat8out, like = no_such_ty...
+                                                             ^
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 0398a19484..0b370235ea 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -45,6 +45,8 @@ ERROR:  column "a" of relation "itest4" must be declared NOT NULL before identit
 ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
 ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS IDENTITY;  -- error, column c does not exist
 ERROR:  column "c" of relation "itest4" does not exist
+LINE 1: ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS ID...
+                                              ^
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- ok
 ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL;  -- error, disallowed
 ERROR:  column "a" of relation "itest4" is an identity column
diff --git a/src/test/regress/expected/typed_table.out b/src/test/regress/expected/typed_table.out
index b6fbda3f21..885f085e15 100644
--- a/src/test/regress/expected/typed_table.out
+++ b/src/test/regress/expected/typed_table.out
@@ -1,5 +1,7 @@
 CREATE TABLE ttable1 OF nothing;
 ERROR:  type "nothing" does not exist
+LINE 1: CREATE TABLE ttable1 OF nothing;
+                                ^
 CREATE TYPE person_type AS (id int, name text);
 CREATE TABLE persons OF person_type;
 CREATE TABLE IF NOT EXISTS persons OF person_type;
@@ -36,6 +38,8 @@ ALTER TABLE persons RENAME COLUMN id TO num;
 ERROR:  cannot rename column of typed table
 ALTER TABLE persons ALTER COLUMN name TYPE varchar;
 ERROR:  cannot alter column type of typed table
+LINE 1: ALTER TABLE persons ALTER COLUMN name TYPE varchar;
+                                         ^
 CREATE TABLE stuff (id int);
 ALTER TABLE persons INHERIT stuff;
 ERROR:  cannot change inheritance of typed table
-- 
2.34.1

v11-0001-Print-out-error-position-for-CreateDomainStmt.patchtext/x-patch; charset=UTF-8; name=v11-0001-Print-out-error-position-for-CreateDomainStmt.patchDownload
From 4b5f688e72f8dd1b6498347cd625c59c926a078e Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Thu, 12 Dec 2024 12:20:51 +0800
Subject: [PATCH v11 1/2] Print out error position for CreateDomainStmt
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

with this patch,
almost of all the user visible errors in DefineDomain will print out error position.

one of example:
CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
ERROR:  collations are not supported by type integer
LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
                                      ^

Author: Kirill Reshke <reshkekirill@gmail.com>
Author: Jian He <jian.universality@gmail.com>
Reviewed-By: Michaël Paquier <michael@paquier.xyz>
Reviewed-By: Álvaro Herrera <alvherre@alvh.no-ip.org>

discussion: https://postgr.es/m/CALdSSPhqfvKbDwqJaY=yEePi_aq61GmMpW88i6ZH7CMG_2Z4Cg@mail.gmail.com
---
 src/backend/commands/typecmds.c               | 58 +++++++++++--------
 src/backend/tcop/utility.c                    |  2 +-
 src/include/commands/typecmds.h               |  2 +-
 .../regress/expected/collate.icu.utf8.out     |  2 +
 .../regress/expected/collate.linux.utf8.out   |  2 +
 src/test/regress/expected/collate.out         |  2 +
 .../expected/collate.windows.win1252.out      |  2 +
 src/test/regress/expected/domain.out          | 26 +++++++++
 8 files changed, 70 insertions(+), 26 deletions(-)

diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index da591c0922..6127313956 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -694,7 +694,7 @@ RemoveTypeById(Oid typeOid)
  *		Registers a new domain.
  */
 ObjectAddress
-DefineDomain(CreateDomainStmt *stmt)
+DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
 {
 	char	   *domainName;
 	char	   *domainArrayName;
@@ -761,7 +761,7 @@ DefineDomain(CreateDomainStmt *stmt)
 	/*
 	 * Look up the base type.
 	 */
-	typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
+	typeTup = typenameType(pstate, stmt->typeName, &basetypeMod);
 	baseType = (Form_pg_type) GETSTRUCT(typeTup);
 	basetypeoid = baseType->oid;
 
@@ -783,7 +783,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("\"%s\" is not a valid base type for a domain",
-						TypeNameToString(stmt->typeName))));
+						TypeNameToString(stmt->typeName)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	aclresult = object_aclcheck(TypeRelationId, basetypeoid, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
@@ -809,7 +810,8 @@ DefineDomain(CreateDomainStmt *stmt)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("collations are not supported by type %s",
-						format_type_be(basetypeoid))));
+						format_type_be(basetypeoid)),
+				 parser_errposition(pstate, stmt->typeName->location)));
 
 	/* passed by value */
 	byValue = baseType->typbyval;
@@ -879,18 +881,15 @@ DefineDomain(CreateDomainStmt *stmt)
 				 */
 				if (saw_default)
 					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("multiple default expressions")));
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("multiple default expressions"),
+							parser_errposition(pstate, constr->location));
 				saw_default = true;
 
 				if (constr->raw_expr)
 				{
-					ParseState *pstate;
 					Node	   *defaultExpr;
 
-					/* Create a dummy ParseState for transformExpr */
-					pstate = make_parsestate(NULL);
-
 					/*
 					 * Cook the constr->raw_expr into an expression. Note:
 					 * name is strictly for error message
@@ -942,12 +941,14 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_NOTNULL:
 				if (nullDefined && !typNotNull)
 					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("conflicting NULL/NOT NULL constraints"),
+							parser_errposition(pstate, constr->location));
 				if (constr->is_no_inherit)
 					ereport(ERROR,
 							errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							errmsg("not-null constraints for domains cannot be marked NO INHERIT"));
+							errmsg("not-null constraints for domains cannot be marked NO INHERIT"),
+							parser_errposition(pstate, constr->location));
 				typNotNull = true;
 				nullDefined = true;
 				break;
@@ -955,8 +956,9 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_NULL:
 				if (nullDefined && typNotNull)
 					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("conflicting NULL/NOT NULL constraints")));
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("conflicting NULL/NOT NULL constraints"),
+							parser_errposition(pstate, constr->location));
 				typNotNull = false;
 				nullDefined = true;
 				break;
@@ -971,8 +973,10 @@ DefineDomain(CreateDomainStmt *stmt)
 				 */
 				if (constr->is_no_inherit)
 					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							 errmsg("check constraints for domains cannot be marked NO INHERIT")));
+							errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+							errmsg("check constraints for domains cannot be marked NO INHERIT"),
+							parser_errposition(pstate, constr->location));
+
 				break;
 
 				/*
@@ -980,26 +984,30 @@ DefineDomain(CreateDomainStmt *stmt)
 				 */
 			case CONSTR_UNIQUE:
 				ereport(ERROR,
-						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("unique constraints not possible for domains")));
+						errcode(ERRCODE_SYNTAX_ERROR),
+						errmsg("unique constraints not possible for domains"),
+						parser_errposition(pstate, constr->location));
 				break;
 
 			case CONSTR_PRIMARY:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("primary key constraints not possible for domains")));
+						 errmsg("primary key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_EXCLUSION:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("exclusion constraints not possible for domains")));
+						 errmsg("exclusion constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_FOREIGN:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("foreign key constraints not possible for domains")));
+						 errmsg("foreign key constraints not possible for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_ATTR_DEFERRABLE:
@@ -1008,14 +1016,16 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_ATTR_IMMEDIATE:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("specifying constraint deferrability not supported for domains")));
+						 errmsg("specifying constraint deferrability not supported for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 			case CONSTR_GENERATED:
 			case CONSTR_IDENTITY:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("specifying GENERATED not supported for domains")));
+						 errmsg("specifying GENERATED not supported for domains"),
+						 parser_errposition(pstate, constr->location)));
 				break;
 
 				/* no default, to let compiler warn about missing case */
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f28bf37105..33dea5a781 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1712,7 +1712,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_CreateDomainStmt:
-				address = DefineDomain((CreateDomainStmt *) parsetree);
+				address = DefineDomain(pstate, (CreateDomainStmt *) parsetree);
 				break;
 
 			case T_CreateConversionStmt:
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index e1b02927c4..cb30d1a258 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -23,7 +23,7 @@
 
 extern ObjectAddress DefineType(ParseState *pstate, List *names, List *parameters);
 extern void RemoveTypeById(Oid typeOid);
-extern ObjectAddress DefineDomain(CreateDomainStmt *stmt);
+extern ObjectAddress DefineDomain(ParseState *pstate, CreateDomainStmt *stmt);
 extern ObjectAddress DefineEnum(CreateEnumStmt *stmt);
 extern ObjectAddress DefineRange(ParseState *pstate, CreateRangeStmt *stmt);
 extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out
index 6cbadafcfb..d4f327636f 100644
--- a/src/test/regress/expected/collate.icu.utf8.out
+++ b/src/test/regress/expected/collate.icu.utf8.out
@@ -120,6 +120,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv-x-icu";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out
index 01664f7c1b..fbaab7cdf8 100644
--- a/src/test/regress/expected/collate.linux.utf8.out
+++ b/src/test/regress/expected/collate.linux.utf8.out
@@ -122,6 +122,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out
index 593a622637..bf72908fbd 100644
--- a/src/test/regress/expected/collate.out
+++ b/src/test/regress/expected/collate.out
@@ -73,6 +73,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "P...
 CREATE DOMAIN testdomain_p AS text COLLATE "POSIX";
 CREATE DOMAIN testdomain_i AS int COLLATE "POSIX"; -- fail
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "POSIX";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_p
diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out
index 31f794988b..4644f56b31 100644
--- a/src/test/regress/expected/collate.windows.win1252.out
+++ b/src/test/regress/expected/collate.windows.win1252.out
@@ -124,6 +124,8 @@ LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails
 ERROR:  collations are not supported by type integer
+LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE";
+                                      ^
 CREATE TABLE collate_test4 (
     a int,
     b testdomain_sv
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index d03ab128e4..7a2a717aea 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -18,30 +18,56 @@ ERROR:  type "domaindroptest" does not exist
 -- some error cases
 create domain d_fail as no_such_type;
 ERROR:  type "no_such_type" does not exist
+LINE 1: create domain d_fail as no_such_type;
+                                ^
 create domain d_fail as int constraint cc REFERENCES this_table_not_exists(i);
 ERROR:  foreign key constraints not possible for domains
+LINE 1: create domain d_fail as int constraint cc REFERENCES this_ta...
+                                    ^
 create domain d_fail as int4 not null no inherit;
 ERROR:  not-null constraints for domains cannot be marked NO INHERIT
+LINE 1: create domain d_fail as int4 not null no inherit;
+                                     ^
 create domain d_fail as int4 not null null;
 ERROR:  conflicting NULL/NOT NULL constraints
+LINE 1: create domain d_fail as int4 not null null;
+                                              ^
 create domain d_fail as int4 not null default 3 default 3;
 ERROR:  multiple default expressions
+LINE 1: create domain d_fail as int4 not null default 3 default 3;
+                                                        ^
 create domain d_fail int4 DEFAULT 3 + 'h';
 ERROR:  invalid input syntax for type integer: "h"
+LINE 1: create domain d_fail int4 DEFAULT 3 + 'h';
+                                              ^
 create domain d_fail int4 collate "C";
 ERROR:  collations are not supported by type integer
+LINE 1: create domain d_fail int4 collate "C";
+                             ^
 create domain d_fail as anyelement;
 ERROR:  "anyelement" is not a valid base type for a domain
+LINE 1: create domain d_fail as anyelement;
+                                ^
 create domain d_fail as int4 unique;
 ERROR:  unique constraints not possible for domains
+LINE 1: create domain d_fail as int4 unique;
+                                     ^
 create domain d_fail as int4 PRIMARY key;
 ERROR:  primary key constraints not possible for domains
+LINE 1: create domain d_fail as int4 PRIMARY key;
+                                     ^
 create domain d_fail as int4 constraint cc generated by default as identity;
 ERROR:  specifying GENERATED not supported for domains
+LINE 1: create domain d_fail as int4 constraint cc generated by defa...
+                                     ^
 create domain d_fail as int4 constraint cc check (values > 1) no inherit;
 ERROR:  check constraints for domains cannot be marked NO INHERIT
+LINE 1: create domain d_fail as int4 constraint cc check (values > 1...
+                                     ^
 create domain d_fail as int4 constraint cc check (values > 1) deferrable;
 ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...n d_fail as int4 constraint cc check (values > 1) deferrable...
+                                                             ^
 -- Test domain input.
 -- Note: the point of checking both INSERT and COPY FROM is that INSERT
 -- exercises CoerceToDomain while COPY exercises domain_in.
-- 
2.34.1

#18Michael Paquier
michael@paquier.xyz
In reply to: jian he (#17)
Re: Pass ParseState as down to utility functions.

On Thu, Dec 12, 2024 at 12:38:06PM +0800, jian he wrote:

I am using DefineDomain(ParseState *pstate, CreateDomainStmt *stmt) for now.
we can also pass querystring or another struct.

All the changes in the alternate outputs for collate are always tricky
to track. As far as I can see, you've not missed a spot. So applied
this one.

0002:
passing AlterTableUtilityContext for some ALTER TABLE subroutine.
add parser_errposition to some existing `ereport(ERROR` places.
-------------------
you mentioned ALTER DOMAIN, I have further simplified it at
/messages/by-id/CACJufxG0N_WLfk-NC_k5w6vv26qLvXupbHvnkKtc2npftJQicQ@mail.gmail.com

-        likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+        likeType = typenameType(pstate, defGetTypeName(likeTypeEl), NULL);

The only test impacted by this change is the CREATE TYPE (LIKE) in
float8. It seems like this should be separated as a change of its own
as it impacts its own command.

For the rest, we're just manipulating ATExecAddOf(),
ATPrepAlterColumnType() and ATExecAlterColumnType(). FWIW, I'm
feeling annoyed with these new make_parsestate() calls, also knowing
that we do it twice for the prep and exec parts of AlterColumnType.
Perhaps that's fine at the end, that's just an increase of calls to
make_parsestate(), still...

like this command will fail, so we don't need to change
create_domain.sgml synopsis section?

Yep, right. I was getting the impression that it would be possible to
have these ones go through with the parser allowed them when I looked
at that last Thursday. Will double-check to be sure.
--
Michael

#19jian he
jian.universality@gmail.com
In reply to: Michael Paquier (#18)
3 attachment(s)
Re: Pass ParseState as down to utility functions.

On Mon, Dec 16, 2024 at 2:15 PM Michael Paquier <michael@paquier.xyz> wrote:

-        likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+        likeType = typenameType(pstate, defGetTypeName(likeTypeEl), NULL);

The only test impacted by this change is the CREATE TYPE (LIKE) in
float8. It seems like this should be separated as a change of its own
as it impacts its own command.

For the rest, we're just manipulating ATExecAddOf(),
ATPrepAlterColumnType() and ATExecAlterColumnType(). FWIW, I'm
feeling annoyed with these new make_parsestate() calls, also knowing
that we do it twice for the prep and exec parts of AlterColumnType.
Perhaps that's fine at the end, that's just an increase of calls to
make_parsestate(), still...

I've removed code changes related to ATExecAddOf.
ATPrepAlterColumnType will catch most of the error, so
ATExecAlterColumnType related change is not necessary.

i've split into 3 patches, feel free to merge them in any way.
v12-0001: add error position for ATPrepAlterColumnType.

v12-0002: add error position for these 3 functions:
transformColumnDefinition, transformAlterTableStmt, transformTableConstraint.

v12-0003: add error position for these 2 functions:
DefineType, transformOfType

like this command will fail, so we don't need to change
create_domain.sgml synopsis section?

Yep, right. I was getting the impression that it would be possible to
have these ones go through with the parser allowed them when I looked
at that last Thursday. Will double-check to be sure.
--

v6-0001-print-out-error-position-for-some-DDL-command.patch
at /messages/by-id/CACJufxGQtN2YzecMx=Odk8+kCTeoN7f=M_kM5e05UDW1H1PbkA@mail.gmail.com
have extensive tests.

Attachments:

v12-0001-add-error-position-for-ATPrepAlterColumnType.patchtext/x-patch; charset=UTF-8; name=v12-0001-add-error-position-for-ATPrepAlterColumnType.patchDownload
From d3bd24b36868186c45183d2ae0bf7e8cc5720dfd Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Mon, 16 Dec 2024 16:11:30 +0800
Subject: [PATCH v12 1/3] add error position for ATPrepAlterColumnType:
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Author: Kirill Reshke <reshkekirill@gmail.com>
Author: Jian He <jian.universality@gmail.com>
Reviewed-By: Michaël Paquier <michael@paquier.xyz>
Reviewed-By: Álvaro Herrera <alvherre@alvh.no-ip.org>

discussion: https://postgr.es/m/CALdSSPhqfvKbDwqJaY=yEePi_aq61GmMpW88i6ZH7CMG_2Z4Cg@mail.gmail.com
---
 src/backend/commands/tablecmds.c          | 12 +++++++-----
 src/test/regress/expected/alter_table.out |  6 ++++++
 src/test/regress/expected/typed_table.out |  2 ++
 3 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6ccae4cb4a..b5e00a9f28 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -13218,10 +13218,12 @@ ATPrepAlterColumnType(List **wqueue,
 	AclResult	aclresult;
 	bool		is_expr;
 
+	pstate->p_sourcetext = context->queryString;
 	if (rel->rd_rel->reloftype && !recursing)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("cannot alter column type of typed table")));
+				 errmsg("cannot alter column type of typed table"),
+				 parser_errposition(pstate, def->location)));
 
 	/* lookup the attribute so we can check inheritance status */
 	tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
@@ -13237,8 +13239,8 @@ ATPrepAlterColumnType(List **wqueue,
 	if (attnum <= 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot alter system column \"%s\"",
-						colName)));
+				 errmsg("cannot alter system column \"%s\"",colName),
+				 parser_errposition(pstate, def->location)));
 
 	/*
 	 * Cannot specify USING when altering type of a generated column, because
@@ -13271,14 +13273,14 @@ ATPrepAlterColumnType(List **wqueue,
 						colName, RelationGetRelationName(rel))));
 
 	/* Look up the target type */
-	typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
+	typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
 
 	aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error_type(aclresult, targettype);
 
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colName, targettype, targetcollid,
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 12852aa612..57263f0709 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3416,10 +3416,16 @@ ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
 -- Some error cases.
 ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
 ERROR:  cannot alter system column "xmin"
+LINE 1: ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+                                              ^
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
 ERROR:  type "x" does not exist
+LINE 1: ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+                                                               ^
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int COLLATE "C";
 ERROR:  collations are not supported by type integer
+LINE 1: ...LE comment_test ALTER COLUMN id SET DATA TYPE int COLLATE "C...
+                                                             ^
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
            comment           
diff --git a/src/test/regress/expected/typed_table.out b/src/test/regress/expected/typed_table.out
index b6fbda3f21..5a81ae202e 100644
--- a/src/test/regress/expected/typed_table.out
+++ b/src/test/regress/expected/typed_table.out
@@ -36,6 +36,8 @@ ALTER TABLE persons RENAME COLUMN id TO num;
 ERROR:  cannot rename column of typed table
 ALTER TABLE persons ALTER COLUMN name TYPE varchar;
 ERROR:  cannot alter column type of typed table
+LINE 1: ALTER TABLE persons ALTER COLUMN name TYPE varchar;
+                                         ^
 CREATE TABLE stuff (id int);
 ALTER TABLE persons INHERIT stuff;
 ERROR:  cannot change inheritance of typed table
-- 
2.34.1

v12-0003-add-error-position-for-these-two-functions.patchtext/x-patch; charset=UTF-8; name=v12-0003-add-error-position-for-these-two-functions.patchDownload
From d6b15cc964cecc9f5b9ac9104f2c3e01db66d029 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Mon, 16 Dec 2024 16:59:07 +0800
Subject: [PATCH v12 3/3] add error position for these two functions:
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

DefineType
transformOfType

instead of passing NULL to typenameType, pass the
ParseState available to typenameType, so error happen
typenameType can report the error position.

Author: Kirill Reshke <reshkekirill@gmail.com>
Author: Jian He <jian.universality@gmail.com>
Reviewed-By: Michaël Paquier <michael@paquier.xyz>
Reviewed-By: Álvaro Herrera <alvherre@alvh.no-ip.org>

discussion: https://postgr.es/m/CALdSSPhqfvKbDwqJaY=yEePi_aq61GmMpW88i6ZH7CMG_2Z4Cg@mail.gmail.com
---
 src/backend/commands/typecmds.c           | 2 +-
 src/backend/parser/parse_utilcmd.c        | 2 +-
 src/test/regress/expected/float8.out      | 2 ++
 src/test/regress/expected/typed_table.out | 2 ++
 4 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 6127313956..4f20b5be06 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -348,7 +348,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 		Type		likeType;
 		Form_pg_type likeForm;
 
-		likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+		likeType = typenameType(pstate, defGetTypeName(likeTypeEl), NULL);
 		likeForm = (Form_pg_type) GETSTRUCT(likeType);
 		internalLength = likeForm->typlen;
 		byValue = likeForm->typbyval;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index b43923c21d..58536b1fe7 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1619,7 +1619,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
 
 	Assert(ofTypename);
 
-	tuple = typenameType(NULL, ofTypename, NULL);
+	tuple = typenameType(cxt->pstate, ofTypename, NULL);
 	check_of_type(tuple);
 	ofTypeId = ((Form_pg_type) GETSTRUCT(tuple))->oid;
 	ofTypename->typeOid = ofTypeId; /* cached for later */
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index 4965ee5554..9ef9793fe9 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -1026,6 +1026,8 @@ LINE 1: create function xfloat8out(xfloat8) returns cstring immutabl...
                                    ^
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = no_such_type);
 ERROR:  type "no_such_type" does not exist
+LINE 1: ...8 (input = xfloat8in, output = xfloat8out, like = no_such_ty...
+                                                             ^
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
diff --git a/src/test/regress/expected/typed_table.out b/src/test/regress/expected/typed_table.out
index 5a81ae202e..885f085e15 100644
--- a/src/test/regress/expected/typed_table.out
+++ b/src/test/regress/expected/typed_table.out
@@ -1,5 +1,7 @@
 CREATE TABLE ttable1 OF nothing;
 ERROR:  type "nothing" does not exist
+LINE 1: CREATE TABLE ttable1 OF nothing;
+                                ^
 CREATE TYPE person_type AS (id int, name text);
 CREATE TABLE persons OF person_type;
 CREATE TABLE IF NOT EXISTS persons OF person_type;
-- 
2.34.1

v12-0002-add-error-position-for-these-3-functions.patchtext/x-patch; charset=UTF-8; name=v12-0002-add-error-position-for-these-3-functions.patchDownload
From 80ead181462d1ed4f897a96c6d9fe8ac74249cb3 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Mon, 16 Dec 2024 16:27:48 +0800
Subject: [PATCH v12 2/3] add error position for these 3 functions:
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

transformColumnDefinition
transformAlterTableStmt
transformTableConstraint

Author: Kirill Reshke <reshkekirill@gmail.com>
Author: Jian He <jian.universality@gmail.com>
Reviewed-By: Michaël Paquier <michael@paquier.xyz>
Reviewed-By: Álvaro Herrera <alvherre@alvh.no-ip.org>

discussion: https://postgr.es/m/CALdSSPhqfvKbDwqJaY=yEePi_aq61GmMpW88i6ZH7CMG_2Z4Cg@mail.gmail.com
---
 src/backend/parser/parse_utilcmd.c        | 18 ++++++++++++------
 src/test/regress/expected/constraints.out | 14 ++++++++++++++
 src/test/regress/expected/identity.out    |  2 ++
 3 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0f324ee4e3..b43923c21d 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -756,7 +756,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 				if (cxt->ispartitioned && constraint->is_no_inherit)
 					ereport(ERROR,
 							errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-							errmsg("not-null constraints on partitioned tables cannot be NO INHERIT"));
+							errmsg("not-null constraints on partitioned tables cannot be NO INHERIT"),
+							parser_errposition(cxt->pstate, constraint->location));
 
 				/* Disallow conflicting [NOT] NULL markings */
 				if (saw_nullable && !column->is_not_null)
@@ -771,7 +772,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 					ereport(ERROR,
 							errcode(ERRCODE_SYNTAX_ERROR),
 							errmsg("conflicting NO INHERIT declarations for not-null constraints on column \"%s\"",
-								   column->colname));
+								   column->colname),
+							parser_errposition(cxt->pstate, constraint->location));
 
 				/*
 				 * If this is the first time we see this column being marked
@@ -806,7 +808,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 						ereport(ERROR,
 								errcode(ERRCODE_SYNTAX_ERROR),
 								errmsg("conflicting NO INHERIT declarations for not-null constraints on column \"%s\"",
-									   column->colname));
+									   column->colname),
+								parser_errposition(cxt->pstate, constraint->location));
 
 					if (!notnull_constraint->conname && constraint->conname)
 						notnull_constraint->conname = constraint->conname;
@@ -1072,7 +1075,8 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
 			if (cxt->ispartitioned && constraint->is_no_inherit)
 				ereport(ERROR,
 						errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						errmsg("not-null constraints on partitioned tables cannot be NO INHERIT"));
+						errmsg("not-null constraints on partitioned tables cannot be NO INHERIT"),
+						parser_errposition(cxt->pstate, constraint->location));
 
 			cxt->nnconstraints = lappend(cxt->nnconstraints, constraint);
 			break;
@@ -3627,7 +3631,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 							ereport(ERROR,
 									(errcode(ERRCODE_UNDEFINED_COLUMN),
 									 errmsg("column \"%s\" of relation \"%s\" does not exist",
-											cmd->name, RelationGetRelationName(rel))));
+											cmd->name, RelationGetRelationName(rel)),
+									 parser_errposition(pstate, def->location)));
 
 						if (attnum > 0 &&
 							TupleDescAttr(tupdesc, attnum - 1)->attidentity)
@@ -3667,7 +3672,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 						ereport(ERROR,
 								(errcode(ERRCODE_UNDEFINED_COLUMN),
 								 errmsg("column \"%s\" of relation \"%s\" does not exist",
-										cmd->name, RelationGetRelationName(rel))));
+										cmd->name, RelationGetRelationName(rel)),
+								 parser_errposition(pstate, def->location)));
 
 					generateSerialExtraStmts(&cxt, newdef,
 											 get_atttype(relid, attnum),
diff --git a/src/test/regress/expected/constraints.out b/src/test/regress/expected/constraints.out
index 71200c90ed..ff3b710468 100644
--- a/src/test/regress/expected/constraints.out
+++ b/src/test/regress/expected/constraints.out
@@ -925,6 +925,8 @@ create table notnull_tbl_fail (a serial constraint foo not null constraint bar n
 ERROR:  conflicting not-null constraint names "foo" and "bar"
 create table notnull_tbl_fail (a serial constraint foo not null no inherit constraint foo not null);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: create table notnull_tbl_fail (a serial constraint foo not n...
+                                                ^
 create table notnull_tbl_fail (a int constraint foo not null, constraint foo not null a no inherit);
 ERROR:  conflicting NO INHERIT declaration for not-null constraint on column "a"
 create table notnull_tbl_fail (a serial constraint foo not null, constraint bar not null a);
@@ -935,12 +937,18 @@ create table notnull_tbl_fail (a serial, constraint foo not null a no inherit);
 ERROR:  conflicting NO INHERIT declaration for not-null constraint on column "a"
 create table notnull_tbl_fail (a serial not null no inherit);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: create table notnull_tbl_fail (a serial not null no inherit)...
+                                                ^
 create table notnull_tbl_fail (like notnull_tbl1, constraint foo2 not null a);
 ERROR:  conflicting not-null constraint names "foo" and "foo2"
 create table notnull_tbl_fail (a int primary key constraint foo not null no inherit);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: create table notnull_tbl_fail (a int primary key constraint ...
+                                                         ^
 create table notnull_tbl_fail (a int not null no inherit primary key);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: create table notnull_tbl_fail (a int not null no inherit pri...
+                                             ^
 create table notnull_tbl_fail (a int primary key, not null a no inherit);
 ERROR:  conflicting NO INHERIT declaration for not-null constraint on column "a"
 create table notnull_tbl_fail (a int, primary key(a), not null a no inherit);
@@ -949,6 +957,8 @@ create table notnull_tbl_fail (a int generated by default as identity, constrain
 ERROR:  conflicting NO INHERIT declaration for not-null constraint on column "a"
 create table notnull_tbl_fail (a int generated by default as identity not null no inherit);
 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: ..._tbl_fail (a int generated by default as identity not null n...
+                                                             ^
 drop table notnull_tbl1;
 -- NOT NULL NO INHERIT
 CREATE TABLE ATACC1 (a int, NOT NULL a NO INHERIT);
@@ -998,8 +1008,12 @@ DROP TABLE ATACC1, ATACC2, ATACC3;
 -- NOT NULL NO INHERIT is not possible on partitioned tables
 CREATE TABLE ATACC1 (a int NOT NULL NO INHERIT) PARTITION BY LIST (a);
 ERROR:  not-null constraints on partitioned tables cannot be NO INHERIT
+LINE 1: CREATE TABLE ATACC1 (a int NOT NULL NO INHERIT) PARTITION BY...
+                                   ^
 CREATE TABLE ATACC1 (a int, NOT NULL a NO INHERIT) PARTITION BY LIST (a);
 ERROR:  not-null constraints on partitioned tables cannot be NO INHERIT
+LINE 1: CREATE TABLE ATACC1 (a int, NOT NULL a NO INHERIT) PARTITION...
+                                    ^
 -- it's not possible to override a no-inherit constraint with an inheritable one
 CREATE TABLE ATACC2 (a int, CONSTRAINT a_is_not_null NOT NULL a NO INHERIT);
 CREATE TABLE ATACC1 (a int);
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 0398a19484..0b370235ea 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -45,6 +45,8 @@ ERROR:  column "a" of relation "itest4" must be declared NOT NULL before identit
 ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
 ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS IDENTITY;  -- error, column c does not exist
 ERROR:  column "c" of relation "itest4" does not exist
+LINE 1: ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS ID...
+                                              ^
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- ok
 ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL;  -- error, disallowed
 ERROR:  column "a" of relation "itest4" is an identity column
-- 
2.34.1

#20Michael Paquier
michael@paquier.xyz
In reply to: jian he (#19)
Re: Pass ParseState as down to utility functions.

On Mon, Dec 16, 2024 at 05:25:45PM +0800, jian he wrote:

i've split into 3 patches, feel free to merge them in any way.
v12-0001: add error position for ATPrepAlterColumnType.

For this one, why don't you do the same for undefined columns and
USING with generated columns at least? This looks half-baked.

v12-0002: add error position for these 3 functions:
transformColumnDefinition, transformAlterTableStmt, transformTableConstraint.

 ERROR:  column "c" of relation "itest4" does not exist
+LINE 1: ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS ID...
+                                              ^

This one is kind of confusing? The part that matters for the error is
the column that does not exist, not the ADD GENERATED.

 ERROR:  conflicting NO INHERIT declarations for not-null constraints on column "a"
+LINE 1: ..._tbl_fail (a int generated by default as identity not null n...
+                                                             ^

This one also, is kind of hard-ish to act on..

v12-0003: add error position for these 2 functions:
DefineType, transformOfType

This one has been applied as of 0f23dedc9176.
--
Michael

#21jian he
jian.universality@gmail.com
In reply to: Michael Paquier (#20)
1 attachment(s)
Re: Pass ParseState as down to utility functions.

On Wed, Dec 25, 2024 at 5:29 PM Michael Paquier <michael@paquier.xyz> wrote:

On Mon, Dec 16, 2024 at 05:25:45PM +0800, jian he wrote:

i've split into 3 patches, feel free to merge them in any way.
v12-0001: add error position for ATPrepAlterColumnType.

For this one, why don't you do the same for undefined columns and
USING with generated columns at least? This looks half-baked.

I think I understand what you mean.
please check attached for changes within ATPrepAlterColumnType

v12-0002: add error position for these 3 functions:
transformColumnDefinition, transformAlterTableStmt, transformTableConstraint.

ERROR:  column "c" of relation "itest4" does not exist
+LINE 1: ALTER TABLE itest4 ALTER COLUMN c ADD GENERATED ALWAYS AS ID...
+                                              ^

This one is kind of confusing? The part that matters for the error is
the column that does not exist, not the ADD GENERATED.

I agree this is confusing.

while looking at 0002:
errmsg("conflicting NO INHERIT declarations for not-null constraints
on column \"%s\"", column->colname)
add parser_errposition
will not be very helpful. i think we need an errhint.

for example:
create table notnull_tbl_fail (a int primary key constraint foo not
null no inherit);
ERROR: conflicting NO INHERIT declarations for not-null constraints
on column "a"
the error message didn't explicitly say that the primary key imply a
not-null inherit constraint.

Maybe we can change to
errmsg("conflicting NO INHERIT declarations for not-null constraints
on column \"%s\"", column->colname),
errhint("specified primary key or identity sequence imply an inherited
not-null constraint will be created")

what do you think?

Attachments:

v13-0001-add-error-position-for-ATPrepAlterColumnType.patchapplication/x-patch; name=v13-0001-add-error-position-for-ATPrepAlterColumnType.patchDownload
From a8055a5292a57e111790629d418fe1663e1ba8ec Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Fri, 27 Dec 2024 13:36:06 +0800
Subject: [PATCH v13 1/1] add error position for ATPrepAlterColumnType
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

within ATPrepAlterColumnType, for various
ereport(ERROR...), add parser_errposition for printing out the error position.

Author: Kirill Reshke <reshkekirill@gmail.com>
Author: Jian He <jian.universality@gmail.com>
Reviewed-By: Michaël Paquier <michael@paquier.xyz>
Reviewed-By: Álvaro Herrera <alvherre@alvh.no-ip.org>

discussion: https://postgr.es/m/CALdSSPhqfvKbDwqJaY=yEePi_aq61GmMpW88i6ZH7CMG_2Z4Cg@mail.gmail.com
---
 src/backend/commands/tablecmds.c              | 25 +++++++++++--------
 src/test/regress/expected/alter_table.out     | 14 +++++++++++
 .../regress/expected/generated_stored.out     |  2 ++
 src/test/regress/expected/typed_table.out     |  2 ++
 4 files changed, 33 insertions(+), 10 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 4937478262..f0acb008ea 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -13222,10 +13222,12 @@ ATPrepAlterColumnType(List **wqueue,
 	AclResult	aclresult;
 	bool		is_expr;
 
+	pstate->p_sourcetext = context->queryString;
 	if (rel->rd_rel->reloftype && !recursing)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("cannot alter column type of typed table")));
+				 errmsg("cannot alter column type of typed table"),
+				 parser_errposition(pstate, def->location)));
 
 	/* lookup the attribute so we can check inheritance status */
 	tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
@@ -13233,7 +13235,8 @@ ATPrepAlterColumnType(List **wqueue,
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_COLUMN),
 				 errmsg("column \"%s\" of relation \"%s\" does not exist",
-						colName, RelationGetRelationName(rel))));
+						colName, RelationGetRelationName(rel)),
+				 parser_errposition(pstate, def->location)));
 	attTup = (Form_pg_attribute) GETSTRUCT(tuple);
 	attnum = attTup->attnum;
 
@@ -13241,8 +13244,8 @@ ATPrepAlterColumnType(List **wqueue,
 	if (attnum <= 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot alter system column \"%s\"",
-						colName)));
+				 errmsg("cannot alter system column \"%s\"",colName),
+				 parser_errposition(pstate, def->location)));
 
 	/*
 	 * Cannot specify USING when altering type of a generated column, because
@@ -13252,7 +13255,8 @@ ATPrepAlterColumnType(List **wqueue,
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
 				 errmsg("cannot specify USING when altering type of generated column"),
-				 errdetail("Column \"%s\" is a generated column.", colName)));
+				 errdetail("Column \"%s\" is a generated column.", colName),
+				 parser_errposition(pstate, def->location)));
 
 	/*
 	 * Don't alter inherited columns.  At outer level, there had better not be
@@ -13262,8 +13266,8 @@ ATPrepAlterColumnType(List **wqueue,
 	if (attTup->attinhcount > 0 && !recursing)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-				 errmsg("cannot alter inherited column \"%s\"",
-						colName)));
+				 errmsg("cannot alter inherited column \"%s\"", colName),
+				 parser_errposition(pstate, def->location)));
 
 	/* Don't alter columns used in the partition key */
 	if (has_partition_attrs(rel,
@@ -13272,17 +13276,18 @@ ATPrepAlterColumnType(List **wqueue,
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 				 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
-						colName, RelationGetRelationName(rel))));
+						colName, RelationGetRelationName(rel)),
+				 parser_errposition(pstate, def->location)));
 
 	/* Look up the target type */
-	typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
+	typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
 
 	aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error_type(aclresult, targettype);
 
 	/* And the collation */
-	targetcollid = GetColumnDefCollation(NULL, def, targettype);
+	targetcollid = GetColumnDefCollation(pstate, def, targettype);
 
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colName, targettype, targetcollid,
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 12852aa612..da74eabccc 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3416,10 +3416,16 @@ ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
 -- Some error cases.
 ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
 ERROR:  cannot alter system column "xmin"
+LINE 1: ALTER TABLE comment_test ALTER COLUMN xmin SET DATA TYPE x;
+                                              ^
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
 ERROR:  type "x" does not exist
+LINE 1: ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE x;
+                                                               ^
 ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int COLLATE "C";
 ERROR:  collations are not supported by type integer
+LINE 1: ...LE comment_test ALTER COLUMN id SET DATA TYPE int COLLATE "C...
+                                                             ^
 -- Check that the comments are intact.
 SELECT col_description('comment_test'::regclass, 1) as comment;
            comment           
@@ -3885,10 +3891,14 @@ ALTER TABLE partitioned DROP COLUMN a;
 ERROR:  cannot drop column "a" because it is part of the partition key of relation "partitioned"
 ALTER TABLE partitioned ALTER COLUMN a TYPE char(5);
 ERROR:  cannot alter column "a" because it is part of the partition key of relation "partitioned"
+LINE 1: ALTER TABLE partitioned ALTER COLUMN a TYPE char(5);
+                                             ^
 ALTER TABLE partitioned DROP COLUMN b;
 ERROR:  cannot drop column "b" because it is part of the partition key of relation "partitioned"
 ALTER TABLE partitioned ALTER COLUMN b TYPE char(5);
 ERROR:  cannot alter column "b" because it is part of the partition key of relation "partitioned"
+LINE 1: ALTER TABLE partitioned ALTER COLUMN b TYPE char(5);
+                                             ^
 -- specifying storage parameters for partitioned tables is not supported
 ALTER TABLE partitioned SET (fillfactor=100);
 ERROR:  cannot specify storage parameters for a partitioned table
@@ -4413,6 +4423,8 @@ ALTER TABLE part_2 RENAME COLUMN b to c;
 ERROR:  cannot rename inherited column "b"
 ALTER TABLE part_2 ALTER COLUMN b TYPE text;
 ERROR:  cannot alter inherited column "b"
+LINE 1: ALTER TABLE part_2 ALTER COLUMN b TYPE text;
+                                        ^
 -- cannot add NOT NULL or check constraints to *only* the parent, when
 -- partitions exist
 ALTER TABLE ONLY list_parted2 ALTER b SET NOT NULL;
@@ -4474,6 +4486,8 @@ ALTER TABLE list_parted2 DROP COLUMN b;
 ERROR:  cannot drop column "b" because it is part of the partition key of relation "part_5"
 ALTER TABLE list_parted2 ALTER COLUMN b TYPE text;
 ERROR:  cannot alter column "b" because it is part of the partition key of relation "part_5"
+LINE 1: ALTER TABLE list_parted2 ALTER COLUMN b TYPE text;
+                                              ^
 -- dropping non-partition key columns should be allowed on the parent table.
 ALTER TABLE list_parted DROP COLUMN b;
 SELECT * FROM list_parted;
diff --git a/src/test/regress/expected/generated_stored.out b/src/test/regress/expected/generated_stored.out
index 0d037d48ca..0dac2a7174 100644
--- a/src/test/regress/expected/generated_stored.out
+++ b/src/test/regress/expected/generated_stored.out
@@ -1032,6 +1032,8 @@ SELECT * FROM gtest27;
 
 ALTER TABLE gtest27 ALTER COLUMN x TYPE boolean USING x <> 0;  -- error
 ERROR:  cannot specify USING when altering type of generated column
+LINE 1: ALTER TABLE gtest27 ALTER COLUMN x TYPE boolean USING x <> 0...
+                                         ^
 DETAIL:  Column "x" is a generated column.
 ALTER TABLE gtest27 ALTER COLUMN x DROP DEFAULT;  -- error
 ERROR:  column "x" of relation "gtest27" is a generated column
diff --git a/src/test/regress/expected/typed_table.out b/src/test/regress/expected/typed_table.out
index aa6150b853..885f085e15 100644
--- a/src/test/regress/expected/typed_table.out
+++ b/src/test/regress/expected/typed_table.out
@@ -38,6 +38,8 @@ ALTER TABLE persons RENAME COLUMN id TO num;
 ERROR:  cannot rename column of typed table
 ALTER TABLE persons ALTER COLUMN name TYPE varchar;
 ERROR:  cannot alter column type of typed table
+LINE 1: ALTER TABLE persons ALTER COLUMN name TYPE varchar;
+                                         ^
 CREATE TABLE stuff (id int);
 ALTER TABLE persons INHERIT stuff;
 ERROR:  cannot change inheritance of typed table
-- 
2.34.1

#22Michael Paquier
michael@paquier.xyz
In reply to: jian he (#21)
Re: Pass ParseState as down to utility functions.

On Fri, Dec 27, 2024 at 03:01:58PM +0800, jian he wrote:

please check attached for changes within ATPrepAlterColumnType

Sorry for the late reply. This is more complete. Two strings are
more complex after doing the coerce_to_target_type(), but honestly I
am not these are worth having a parser_errposition() as they also have
an errhint().

Maybe we can change to
errmsg("conflicting NO INHERIT declarations for not-null constraints
on column \"%s\"", column->colname),
errhint("specified primary key or identity sequence imply an inherited
not-null constraint will be created")

what do you think?

Hmm. Not sure that this is an improvement. Will think a bit more
about it.
--
Michael