diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 8a95d4710b700d7650ef404bed6df882f9c47f79..cb1d029bf54726cd68240e03dcf9f84bcc45b20c 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -432,7 +432,9 @@ crosstab(PG_FUNCTION_ARGS)
 			break;
 		default:
 			/* result type isn't composite */
-			elog(ERROR, "return type must be a row type");
+			ereport(ERROR,
+					(errcode(ERRCODE_DATATYPE_MISMATCH),
+					 errmsg("return type must be a row type")));
 			break;
 	}
 
@@ -1350,7 +1352,9 @@ build_tuplestore_recursively(char *key_fld,
 				appendStringInfo(&chk_current_key, "%s%s%s",
 								 branch_delim, current_key, branch_delim);
 				if (strstr(chk_branchstr.data, chk_current_key.data))
-					elog(ERROR, "infinite recursion detected");
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_RECURSION),
+							 errmsg("infinite recursion detected")));
 			}
 
 			/* OK, extend the branch */
@@ -1429,7 +1433,7 @@ validateConnectbyTupleDesc(TupleDesc tupdesc, bool show_branch, bool show_serial
 	{
 		if (tupdesc->natts != (CONNECTBY_NCOLS + serial_column))
 			ereport(ERROR,
-					(errcode(ERRCODE_SYNTAX_ERROR),
+					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("invalid return type"),
 					 errdetail("Query-specified return tuple has " \
 							   "wrong number of columns.")));
@@ -1438,7 +1442,7 @@ validateConnectbyTupleDesc(TupleDesc tupdesc, bool show_branch, bool show_serial
 	{
 		if (tupdesc->natts != CONNECTBY_NCOLS_NOBRANCH + serial_column)
 			ereport(ERROR,
-					(errcode(ERRCODE_SYNTAX_ERROR),
+					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("invalid return type"),
 					 errdetail("Query-specified return tuple has " \
 							   "wrong number of columns.")));
@@ -1447,14 +1451,14 @@ validateConnectbyTupleDesc(TupleDesc tupdesc, bool show_branch, bool show_serial
 	/* check that the types of the first two columns match */
 	if (tupdesc->attrs[0]->atttypid != tupdesc->attrs[1]->atttypid)
 		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("invalid return type"),
 				 errdetail("First two columns must be the same type.")));
 
 	/* check that the type of the third column is INT4 */
 	if (tupdesc->attrs[2]->atttypid != INT4OID)
 		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("invalid return type"),
 				 errdetail("Third column must be type %s.",
 						   format_type_be(INT4OID))));
@@ -1462,20 +1466,26 @@ validateConnectbyTupleDesc(TupleDesc tupdesc, bool show_branch, bool show_serial
 	/* check that the type of the fourth column is TEXT if applicable */
 	if (show_branch && tupdesc->attrs[3]->atttypid != TEXTOID)
 		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("invalid return type"),
 				 errdetail("Fourth column must be type %s.",
 						   format_type_be(TEXTOID))));
 
 	/* check that the type of the fifth column is INT4 */
 	if (show_branch && show_serial && tupdesc->attrs[4]->atttypid != INT4OID)
-		elog(ERROR, "query-specified return tuple not valid for Connectby: "
-			 "fifth column must be type %s", format_type_be(INT4OID));
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+			  errmsg("query-specified return tuple not valid for Connectby: "
+					 "fifth column must be type %s",
+					 format_type_be(INT4OID))));
 
 	/* check that the type of the fifth column is INT4 */
 	if (!show_branch && show_serial && tupdesc->attrs[3]->atttypid != INT4OID)
-		elog(ERROR, "query-specified return tuple not valid for Connectby: "
-			 "fourth column must be type %s", format_type_be(INT4OID));
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+			  errmsg("query-specified return tuple not valid for Connectby: "
+					 "fourth column must be type %s",
+					 format_type_be(INT4OID))));
 
 	/* OK, the tupdesc is valid for our purposes */
 }
@@ -1496,7 +1506,7 @@ compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
 	 */
 	if (sql_tupdesc->natts < 2)
 		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("invalid return type"),
 				 errdetail("Query must return at least two columns.")));
 
@@ -1511,7 +1521,7 @@ compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
 	if (ret_atttypid != sql_atttypid ||
 		(ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
 		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("invalid return type"),
 				 errdetail("SQL key field type %s does " \
 						   "not match return key field type %s.",
@@ -1525,7 +1535,7 @@ compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
 	if (ret_atttypid != sql_atttypid ||
 		(ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
 		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("invalid return type"),
 				 errdetail("SQL parent key field type %s does " \
 						   "not match return parent key field type %s.",
@@ -1556,7 +1566,7 @@ compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
 	sql_atttypid = sql_tupdesc->attrs[0]->atttypid;
 	if (ret_atttypid != sql_atttypid)
 		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("invalid return type"),
 				 errdetail("SQL rowid datatype does not match " \
 						   "return rowid datatype.")));
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 8176b6a6d414b2ac2e0dd69c1db9be4dbcb9c388..180f529060d1a502e3a52fe916626bba77fa2f1c 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -484,7 +484,7 @@ allocate_reloption(bits32 kinds, int type, char *name, char *desc)
 			size = sizeof(relopt_string);
 			break;
 		default:
-			elog(ERROR, "unsupported option type");
+			elog(ERROR, "unsupported reloption type %d", type);
 			return NULL;		/* keep compiler quiet */
 	}
 
@@ -1016,7 +1016,8 @@ parse_one_reloption(relopt_value *option, char *text_str, int text_len,
 				parsed = parse_bool(value, &option->values.bool_val);
 				if (validate && !parsed)
 					ereport(ERROR,
-					   (errmsg("invalid value for boolean option \"%s\": %s",
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("invalid value for boolean option \"%s\": %s",
 							   option->gen->name, value)));
 			}
 			break;
@@ -1027,12 +1028,14 @@ parse_one_reloption(relopt_value *option, char *text_str, int text_len,
 				parsed = parse_int(value, &option->values.int_val, 0, NULL);
 				if (validate && !parsed)
 					ereport(ERROR,
-					   (errmsg("invalid value for integer option \"%s\": %s",
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("invalid value for integer option \"%s\": %s",
 							   option->gen->name, value)));
 				if (validate && (option->values.int_val < optint->min ||
 								 option->values.int_val > optint->max))
 					ereport(ERROR,
-						  (errmsg("value %s out of bounds for option \"%s\"",
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						   errmsg("value %s out of bounds for option \"%s\"",
 								  value, option->gen->name),
 					 errdetail("Valid values are between \"%d\" and \"%d\".",
 							   optint->min, optint->max)));
@@ -1045,12 +1048,14 @@ parse_one_reloption(relopt_value *option, char *text_str, int text_len,
 				parsed = parse_real(value, &option->values.real_val);
 				if (validate && !parsed)
 					ereport(ERROR,
-							(errmsg("invalid value for floating point option \"%s\": %s",
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("invalid value for floating point option \"%s\": %s",
 									option->gen->name, value)));
 				if (validate && (option->values.real_val < optreal->min ||
 								 option->values.real_val > optreal->max))
 					ereport(ERROR,
-						  (errmsg("value %s out of bounds for option \"%s\"",
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						   errmsg("value %s out of bounds for option \"%s\"",
 								  value, option->gen->name),
 					 errdetail("Valid values are between \"%f\" and \"%f\".",
 							   optreal->min, optreal->max)));
@@ -1168,7 +1173,7 @@ fillRelOptions(void *rdopts, Size basesize,
 						}
 						break;
 					default:
-						elog(ERROR, "unrecognized reloption type %c",
+						elog(ERROR, "unsupported reloption type %d",
 							 options[i].gen->type);
 						break;
 				}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 050efdc4806a716df0f0515619fe7a04bd9577d5..3701d8e59d5368a664e71b32829d34180f002860 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2801,7 +2801,9 @@ l1:
 	if (result == HeapTupleInvisible)
 	{
 		UnlockReleaseBuffer(buffer);
-		elog(ERROR, "attempted to delete invisible tuple");
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("attempted to delete invisible tuple")));
 	}
 	else if (result == HeapTupleBeingUpdated && wait)
 	{
@@ -3343,7 +3345,9 @@ l2:
 	if (result == HeapTupleInvisible)
 	{
 		UnlockReleaseBuffer(buffer);
-		elog(ERROR, "attempted to update invisible tuple");
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("attempted to update invisible tuple")));
 	}
 	else if (result == HeapTupleBeingUpdated && wait)
 	{
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 47dd3accafe24a02272dd83571a60fcec1dfcfac..8db1b35fe82d987270b529fa3c84097b28b860b4 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1422,9 +1422,9 @@ BeginCopy(bool is_from,
 		 * in any RLS clauses.
 		 *
 		 * When this happens, we are passed in the relid of the originally
-		 * found relation (which we have locked).  As the planner will look
-		 * up the relation again, we double-check here to make sure it found
-		 * the same one that we have locked.
+		 * found relation (which we have locked).  As the planner will look up
+		 * the relation again, we double-check here to make sure it found the
+		 * same one that we have locked.
 		 */
 		if (queryRelId != InvalidOid)
 		{
@@ -1603,10 +1603,12 @@ ClosePipeToProgram(CopyState cstate)
 	pclose_rc = ClosePipeStream(cstate->copy_file);
 	if (pclose_rc == -1)
 		ereport(ERROR,
-				(errmsg("could not close pipe to external command: %m")));
+				(errcode_for_file_access(),
+				 errmsg("could not close pipe to external command: %m")));
 	else if (pclose_rc != 0)
 		ereport(ERROR,
-				(errmsg("program \"%s\" failed",
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("program \"%s\" failed",
 						cstate->filename),
 				 errdetail_internal("%s", wait_result_to_str(pclose_rc))));
 }
@@ -1703,7 +1705,8 @@ BeginCopyTo(Relation rel,
 			cstate->copy_file = OpenPipeStream(cstate->filename, PG_BINARY_W);
 			if (cstate->copy_file == NULL)
 				ereport(ERROR,
-						(errmsg("could not execute command \"%s\": %m",
+						(errcode_for_file_access(),
+						 errmsg("could not execute command \"%s\": %m",
 								cstate->filename)));
 		}
 		else
@@ -1730,7 +1733,10 @@ BeginCopyTo(Relation rel,
 								cstate->filename)));
 
 			if (fstat(fileno(cstate->copy_file), &st))
-				elog(ERROR, "could not stat file \"%s\": %m", cstate->filename);
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not stat file \"%s\": %m",
+								cstate->filename)));
 
 			if (S_ISDIR(st.st_mode))
 				ereport(ERROR,
@@ -2271,13 +2277,13 @@ CopyFrom(CopyState cstate)
 	{
 		if (!ThereAreNoPriorRegisteredSnapshots() || !ThereAreNoReadyPortals())
 			ereport(ERROR,
-					(ERRCODE_INVALID_TRANSACTION_STATE,
+					(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
 					 errmsg("cannot perform FREEZE because of prior transaction activity")));
 
 		if (cstate->rel->rd_createSubid != GetCurrentSubTransactionId() &&
 		 cstate->rel->rd_newRelfilenodeSubid != GetCurrentSubTransactionId())
 			ereport(ERROR,
-					(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 					 errmsg("cannot perform FREEZE because the table was not created or truncated in the current subtransaction")));
 
 		hi_options |= HEAP_INSERT_FROZEN;
@@ -2737,7 +2743,8 @@ BeginCopyFrom(Relation rel,
 			cstate->copy_file = OpenPipeStream(cstate->filename, PG_BINARY_R);
 			if (cstate->copy_file == NULL)
 				ereport(ERROR,
-						(errmsg("could not execute command \"%s\": %m",
+						(errcode_for_file_access(),
+						 errmsg("could not execute command \"%s\": %m",
 								cstate->filename)));
 		}
 		else
@@ -2752,7 +2759,10 @@ BeginCopyFrom(Relation rel,
 								cstate->filename)));
 
 			if (fstat(fileno(cstate->copy_file), &st))
-				elog(ERROR, "could not stat file \"%s\": %m", cstate->filename);
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not stat file \"%s\": %m",
+								cstate->filename)));
 
 			if (S_ISDIR(st.st_mode))
 				ereport(ERROR,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index baf66f1e6c01e6db1ae4b9b06eda85f424545b12..85b048324794864bc16de8a1c77c7ce7638d3011 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -180,7 +180,10 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
 	 * calls a hostile index expression that itself calls ANALYZE.
 	 */
 	if (in_vacuum)
-		elog(ERROR, "%s cannot be executed from VACUUM or ANALYZE", stmttype);
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("%s cannot be executed from VACUUM or ANALYZE",
+						stmttype)));
 
 	/*
 	 * Send info about dead objects to the statistics collector, unless we are
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 0f911f210bf3bae630223bba0202a23e532dcf58..16bc8fa5f6c3534191d08551050e76ff9d765969 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -631,7 +631,8 @@ ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
 		{
 			if (variable->vartype != attr->atttypid)
 				ereport(ERROR,
-						(errmsg("attribute %d has wrong type", attnum),
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("attribute %d has wrong type", attnum),
 						 errdetail("Table has type %s, but query expects %s.",
 								   format_type_be(attr->atttypid),
 								   format_type_be(variable->vartype))));
@@ -4111,7 +4112,8 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
 	/* As in ExecEvalScalarVar, we should but can't check typmod */
 	if (fselect->resulttype != attr->atttypid)
 		ereport(ERROR,
-				(errmsg("attribute %d has wrong type", fieldnum),
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("attribute %d has wrong type", fieldnum),
 				 errdetail("Table has type %s, but query expects %s.",
 						   format_type_be(attr->atttypid),
 						   format_type_be(fselect->resulttype))));
diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c
index ce1d9abddea93acee5014e87cf072868df0d7d08..ba4b48298fdddbe2b7770e238167df71e3fe6626 100644
--- a/src/backend/utils/adt/txid.c
+++ b/src/backend/utils/adt/txid.c
@@ -334,8 +334,11 @@ parse_snapshot(const char *str)
 	return buf_finalize(buf);
 
 bad_format:
-	elog(ERROR, "invalid input for txid_snapshot: \"%s\"", str_start);
-	return NULL;
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("invalid input syntax for type txid_snapshot: \"%s\"",
+					str_start)));
+	return NULL;				/* keep compiler quiet */
 }
 
 /*
@@ -526,8 +529,10 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
 	PG_RETURN_POINTER(snap);
 
 bad_format:
-	elog(ERROR, "invalid snapshot data");
-	return (Datum) NULL;
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+			 errmsg("invalid external txid_snapshot data")));
+	PG_RETURN_POINTER(NULL);	/* keep compiler quiet */
 }
 
 /*
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 78baaac05db270a7800088e137d9b435d03c7fc8..ae0ba19814fd03d75063495f32a06dae8a6cada5 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -640,8 +640,9 @@ select_perl_context(bool trusted)
 		else
 			plperl_untrusted_init();
 #else
-		elog(ERROR,
-			 "cannot allocate multiple Perl interpreters on this platform");
+		errmsg(ERROR,
+			   (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				errmsg("cannot allocate multiple Perl interpreters on this platform")));
 #endif
 	}
 
@@ -660,7 +661,8 @@ select_perl_context(bool trusted)
 	eval_pv("PostgreSQL::InServer::SPI::bootstrap()", FALSE);
 	if (SvTRUE(ERRSV))
 		ereport(ERROR,
-				(errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
 		errcontext("while executing PostgreSQL::InServer::SPI::bootstrap")));
 
 	/* Fully initialized, so mark the hashtable entry valid */
@@ -834,12 +836,14 @@ plperl_init_interp(void)
 	if (perl_parse(plperl, plperl_init_shared_libs,
 				   nargs, embedding, NULL) != 0)
 		ereport(ERROR,
-				(errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
 				 errcontext("while parsing Perl initialization")));
 
 	if (perl_run(plperl) != 0)
 		ereport(ERROR,
-				(errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
 				 errcontext("while running Perl initialization")));
 
 #ifdef PLPERL_RESTORE_LOCALE
@@ -952,7 +956,8 @@ plperl_trusted_init(void)
 	eval_pv(PLC_TRUSTED, FALSE);
 	if (SvTRUE(ERRSV))
 		ereport(ERROR,
-				(errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
 				 errcontext("while executing PLC_TRUSTED")));
 
 	/*
@@ -963,7 +968,8 @@ plperl_trusted_init(void)
 	eval_pv("my $a=chr(0x100); return $a =~ /\\xa9/i", FALSE);
 	if (SvTRUE(ERRSV))
 		ereport(ERROR,
-				(errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
 				 errcontext("while executing utf8fix")));
 
 	/*
@@ -1002,11 +1008,12 @@ plperl_trusted_init(void)
 	if (plperl_on_plperl_init && *plperl_on_plperl_init)
 	{
 		eval_pv(plperl_on_plperl_init, FALSE);
+		/* XXX need to find a way to determine a better errcode here */
 		if (SvTRUE(ERRSV))
 			ereport(ERROR,
-					(errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
+					(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+					 errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
 					 errcontext("while executing plperl.on_plperl_init")));
-
 	}
 }
 
@@ -1025,7 +1032,8 @@ plperl_untrusted_init(void)
 		eval_pv(plperl_on_plperlu_init, FALSE);
 		if (SvTRUE(ERRSV))
 			ereport(ERROR,
-					(errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
+					(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+					 errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
 					 errcontext("while executing plperl.on_plperlu_init")));
 	}
 }
@@ -1382,7 +1390,9 @@ plperl_sv_to_literal(SV *sv, char *fqtypename)
 				isnull;
 
 	if (!OidIsValid(typid))
-		elog(ERROR, "lookup failed for type %s", fqtypename);
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("lookup failed for type %s", fqtypename)));
 
 	datum = plperl_sv_to_datum(sv,
 							   typid, -1,
@@ -2059,7 +2069,8 @@ plperl_create_sub(plperl_proc_desc *prodesc, char *s, Oid fn_oid)
 
 	if (!subref)
 		ereport(ERROR,
-		(errmsg("didn't get a CODE reference from compiling function \"%s\"",
+				(errcode(ERRCODE_SYNTAX_ERROR),
+		 errmsg("didn't get a CODE reference from compiling function \"%s\"",
 				prodesc->proname)));
 
 	prodesc->reference = subref;
@@ -2147,7 +2158,9 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
 		PUTBACK;
 		FREETMPS;
 		LEAVE;
-		elog(ERROR, "didn't get a return item from function");
+		ereport(ERROR,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("didn't get a return item from function")));
 	}
 
 	if (SvTRUE(ERRSV))
@@ -2156,9 +2169,10 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
 		PUTBACK;
 		FREETMPS;
 		LEAVE;
-		/* XXX need to find a way to assign an errcode here */
+		/* XXX need to find a way to determine a better errcode here */
 		ereport(ERROR,
-				(errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV)))));
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV)))));
 	}
 
 	retval = newSVsv(POPs);
@@ -2187,7 +2201,9 @@ plperl_call_perl_trigger_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo,
 
 	TDsv = get_sv("main::_TD", 0);
 	if (!TDsv)
-		elog(ERROR, "couldn't fetch $_TD");
+		ereport(ERROR,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("couldn't fetch $_TD")));
 
 	save_item(TDsv);			/* local $_TD */
 	sv_setsv(TDsv, td);
@@ -2209,7 +2225,9 @@ plperl_call_perl_trigger_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo,
 		PUTBACK;
 		FREETMPS;
 		LEAVE;
-		elog(ERROR, "didn't get a return item from trigger function");
+		ereport(ERROR,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("didn't get a return item from trigger function")));
 	}
 
 	if (SvTRUE(ERRSV))
@@ -2218,9 +2236,10 @@ plperl_call_perl_trigger_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo,
 		PUTBACK;
 		FREETMPS;
 		LEAVE;
-		/* XXX need to find a way to assign an errcode here */
+		/* XXX need to find a way to determine a better errcode here */
 		ereport(ERROR,
-				(errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV)))));
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV)))));
 	}
 
 	retval = newSVsv(POPs);
@@ -2248,7 +2267,9 @@ plperl_call_perl_event_trigger_func(plperl_proc_desc *desc,
 
 	TDsv = get_sv("main::_TD", 0);
 	if (!TDsv)
-		elog(ERROR, "couldn't fetch $_TD");
+		ereport(ERROR,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("couldn't fetch $_TD")));
 
 	save_item(TDsv);			/* local $_TD */
 	sv_setsv(TDsv, td);
@@ -2266,7 +2287,9 @@ plperl_call_perl_event_trigger_func(plperl_proc_desc *desc,
 		PUTBACK;
 		FREETMPS;
 		LEAVE;
-		elog(ERROR, "didn't get a return item from trigger function");
+		ereport(ERROR,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("didn't get a return item from trigger function")));
 	}
 
 	if (SvTRUE(ERRSV))
@@ -2275,9 +2298,10 @@ plperl_call_perl_event_trigger_func(plperl_proc_desc *desc,
 		PUTBACK;
 		FREETMPS;
 		LEAVE;
-		/* XXX need to find a way to assign an errcode here */
+		/* XXX need to find a way to determine a better errcode here */
 		ereport(ERROR,
-				(errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV)))));
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV)))));
 	}
 
 	retval = newSVsv(POPs);
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index 461d68c5461f642c1a31c8524f65cb55582b4491..15406d60b9012a5e48ae99e802b8c10d567fe46c 100644
--- a/src/pl/plpython/plpy_elog.c
+++ b/src/pl/plpython/plpy_elog.c
@@ -97,7 +97,7 @@ PLy_elog(int elevel, const char *fmt,...)
 	PG_TRY();
 	{
 		ereport(elevel,
-				(errcode(sqlerrcode ? sqlerrcode : ERRCODE_INTERNAL_ERROR),
+				(errcode(sqlerrcode ? sqlerrcode : ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
 			  errmsg_internal("%s", primary ? primary : "no exception data"),
 				 (detail) ? errdetail_internal("%s", detail) : 0,
 				 (tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0,
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index 8c525c932a7e50a9b895cd07d7e09042304f560e..3ccebe403e428ad5281e3f3a11b73272fc21978f 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -662,11 +662,13 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
 	{
 		if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL)
 			ereport(ERROR,
-					(errmsg("TD[\"new\"] deleted, cannot modify row")));
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("TD[\"new\"] deleted, cannot modify row")));
 		Py_INCREF(plntup);
 		if (!PyDict_Check(plntup))
 			ereport(ERROR,
-					(errmsg("TD[\"new\"] is not a dictionary")));
+					(errcode(ERRCODE_DATATYPE_MISMATCH),
+					 errmsg("TD[\"new\"] is not a dictionary")));
 
 		plkeys = PyDict_Keys(plntup);
 		natts = PyList_Size(plkeys);
@@ -690,13 +692,15 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
 			else
 			{
 				ereport(ERROR,
-						(errmsg("TD[\"new\"] dictionary key at ordinal position %d is not a string", i)));
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("TD[\"new\"] dictionary key at ordinal position %d is not a string", i)));
 				plattstr = NULL;	/* keep compiler quiet */
 			}
 			attn = SPI_fnumber(tupdesc, plattstr);
 			if (attn == SPI_ERROR_NOATTRIBUTE)
 				ereport(ERROR,
-						(errmsg("key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row",
+						(errcode(ERRCODE_UNDEFINED_COLUMN),
+						 errmsg("key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row",
 								plattstr)));
 			atti = attn - 1;
 
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 48a3206da1ce8e51009889e37f153a5db110d7b6..edfda5915b24f06108cf35d6c482f72516183c28 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -551,7 +551,8 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
 	if (SPI_processed == 0)
 	{
 		SPI_freetuptable(SPI_tuptable);
-		elog(WARNING, "module \"unknown\" not found in pltcl_modules");
+		ereport(WARNING,
+				(errmsg("module \"unknown\" not found in pltcl_modules")));
 		relation_close(pmrel, AccessShareLock);
 		return;
 	}
@@ -585,8 +586,10 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
 	if (tcl_rc != TCL_OK)
 	{
 		UTF_BEGIN;
-		elog(ERROR, "could not load module \"unknown\": %s",
-			 UTF_U2E(Tcl_GetStringResult(interp)));
+		ereport(ERROR,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("could not load module \"unknown\": %s",
+						UTF_U2E(Tcl_GetStringResult(interp)))));
 		UTF_END;
 	}
 
@@ -1039,8 +1042,10 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
 					  &ret_numvals, &ret_values) != TCL_OK)
 	{
 		UTF_BEGIN;
-		elog(ERROR, "could not split return value from trigger: %s",
-			 UTF_U2E(Tcl_GetStringResult(interp)));
+		ereport(ERROR,
+				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
+				 errmsg("could not split return value from trigger: %s",
+						UTF_U2E(Tcl_GetStringResult(interp)))));
 		UTF_END;
 	}
 
@@ -1048,7 +1053,9 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
 	PG_TRY();
 	{
 		if (ret_numvals % 2 != 0)
-			elog(ERROR, "invalid return list from trigger - must have even # of elements");
+			ereport(ERROR,
+					(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
+					 errmsg("invalid return list from trigger - must have even # of elements")));
 
 		modattrs = (int *) palloc(tupdesc->natts * sizeof(int));
 		modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum));
@@ -1082,9 +1089,15 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
 			 ************************************************************/
 			attnum = SPI_fnumber(tupdesc, ret_name);
 			if (attnum == SPI_ERROR_NOATTRIBUTE)
-				elog(ERROR, "invalid attribute \"%s\"", ret_name);
+				ereport(ERROR,
+						(errcode(ERRCODE_UNDEFINED_COLUMN),
+						 errmsg("unrecognized attribute \"%s\"",
+								ret_name)));
 			if (attnum <= 0)
-				elog(ERROR, "cannot set system attribute \"%s\"", ret_name);
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("cannot set system attribute \"%s\"",
+								ret_name)));
 
 			/************************************************************
 			 * Ignore dropped columns
@@ -1205,7 +1218,8 @@ throw_tcl_error(Tcl_Interp *interp, const char *proname)
 	econtext = UTF_U2E((char *) Tcl_GetVar(interp, "errorInfo",
 										   TCL_GLOBAL_ONLY));
 	ereport(ERROR,
-			(errmsg("%s", emsg),
+			(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+			 errmsg("%s", emsg),
 			 errcontext("%s\nin PL/Tcl function \"%s\"",
 						econtext, proname)));
 	UTF_END;
@@ -1545,8 +1559,11 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
 			free(prodesc->internal_proname);
 			free(prodesc);
 			UTF_BEGIN;
-			elog(ERROR, "could not create internal procedure \"%s\": %s",
-				 internal_proname, UTF_U2E(Tcl_GetStringResult(interp)));
+			ereport(ERROR,
+					(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+					 errmsg("could not create internal procedure \"%s\": %s",
+							internal_proname,
+							UTF_U2E(Tcl_GetStringResult(interp)))));
 			UTF_END;
 		}
 
@@ -1614,10 +1631,10 @@ pltcl_elog(ClientData cdata, Tcl_Interp *interp,
 	}
 
 	/*
-	 * For non-error messages, just pass 'em to elog().  We do not expect that
-	 * this will fail, but just on the off chance it does, report the error
-	 * back to Tcl.  Note we are assuming that elog() can't have any internal
-	 * failures that are so bad as to require a transaction abort.
+	 * For non-error messages, just pass 'em to ereport().  We do not expect
+	 * that this will fail, but just on the off chance it does, report the
+	 * error back to Tcl.  Note we are assuming that ereport() can't have any
+	 * internal failures that are so bad as to require a transaction abort.
 	 *
 	 * This path is also used for FATAL errors, which aren't going to come
 	 * back to us at all.
@@ -1626,7 +1643,9 @@ pltcl_elog(ClientData cdata, Tcl_Interp *interp,
 	PG_TRY();
 	{
 		UTF_BEGIN;
-		elog(level, "%s", UTF_U2E(argv[2]));
+		ereport(level,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+				 errmsg("%s", UTF_U2E(argv[2]))));
 		UTF_END;
 	}
 	PG_CATCH();
diff --git a/src/test/regress/expected/txid.out b/src/test/regress/expected/txid.out
index 7750b7b98f937b2c81a80dc0dba91e34a34c33fb..ddd217eb1024f6ddb595155838ff698d36e4618f 100644
--- a/src/test/regress/expected/txid.out
+++ b/src/test/regress/expected/txid.out
@@ -20,19 +20,19 @@ select '12:16:14,14'::txid_snapshot;
 
 -- errors
 select '31:12:'::txid_snapshot;
-ERROR:  invalid input for txid_snapshot: "31:12:"
+ERROR:  invalid input syntax for type txid_snapshot: "31:12:"
 LINE 1: select '31:12:'::txid_snapshot;
                ^
 select '0:1:'::txid_snapshot;
-ERROR:  invalid input for txid_snapshot: "0:1:"
+ERROR:  invalid input syntax for type txid_snapshot: "0:1:"
 LINE 1: select '0:1:'::txid_snapshot;
                ^
 select '12:13:0'::txid_snapshot;
-ERROR:  invalid input for txid_snapshot: "12:13:0"
+ERROR:  invalid input syntax for type txid_snapshot: "12:13:0"
 LINE 1: select '12:13:0'::txid_snapshot;
                ^
 select '12:16:14,13'::txid_snapshot;
-ERROR:  invalid input for txid_snapshot: "12:16:14,13"
+ERROR:  invalid input syntax for type txid_snapshot: "12:16:14,13"
 LINE 1: select '12:16:14,13'::txid_snapshot;
                ^
 create temp table snapshot_test (
@@ -235,6 +235,6 @@ SELECT txid_snapshot '1:9223372036854775807:3';
 (1 row)
 
 SELECT txid_snapshot '1:9223372036854775808:3';
-ERROR:  invalid input for txid_snapshot: "1:9223372036854775808:3"
+ERROR:  invalid input syntax for type txid_snapshot: "1:9223372036854775808:3"
 LINE 1: SELECT txid_snapshot '1:9223372036854775808:3';
                              ^
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index bd31a3d3825266cb5030cef080ee72a19e435d13..476975f391afc9e29aacd0f9de862e26ebfaa8bb 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -549,8 +549,10 @@ ttdummy(PG_FUNCTION_ARGS)
 			elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[1]);
 
 		if (oldon != newon || oldoff != newoff)
-			elog(ERROR, "ttdummy (%s): you cannot change %s and/or %s columns (use set_ttdummy)",
-				 relname, args[0], args[1]);
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("ttdummy (%s): you cannot change %s and/or %s columns (use set_ttdummy)",
+							relname, args[0], args[1])));
 
 		if (newoff != TTDUMMY_INFINITY)
 		{