From 557ccc6fdaa0b202f69eeba4407b9373b00886e0 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Thu, 19 Jan 2017 16:45:37 -0800 Subject: [PATCH] Support for deferred error messages. It can be useful, e.g. in the upcoming prepared statement support, to be able to return an error from a function that is not raised immediately, but can later be thrown. That allows e.g. to attempt to plan a statment using different methods and to create good error messages in each planner, but to only error out after all planners have been run. To enable that create support for deferred error messages that can be created (supporting errorcode, message, detail, hint) in one function, and then thrown in different place. --- .../distributed/utils/citus_nodefuncs.c | 5 +- .../distributed/utils/citus_outfuncs.c | 24 ++++++++ .../distributed/utils/citus_readfuncs.c | 19 ++++++ .../distributed/utils/citus_readfuncs_95.c | 2 + src/backend/distributed/utils/errormessage.c | 58 ++++++++++++++++++ src/include/distributed/citus_nodefuncs.h | 2 + src/include/distributed/citus_nodes.h | 5 +- src/include/distributed/errormessage.h | 60 +++++++++++++++++++ 8 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 src/backend/distributed/utils/errormessage.c create mode 100644 src/include/distributed/errormessage.h diff --git a/src/backend/distributed/utils/citus_nodefuncs.c b/src/backend/distributed/utils/citus_nodefuncs.c index 03dda8a7c..42c7a4ece 100644 --- a/src/backend/distributed/utils/citus_nodefuncs.c +++ b/src/backend/distributed/utils/citus_nodefuncs.c @@ -13,6 +13,7 @@ #include "catalog/pg_type.h" #include "distributed/citus_nodes.h" #include "distributed/citus_nodefuncs.h" +#include "distributed/errormessage.h" #include "distributed/metadata_cache.h" #include "distributed/multi_planner.h" @@ -33,7 +34,8 @@ static const char *CitusNodeTagNamesD[] = { "Task", "ShardInterval", "ShardPlacement", - "RelationShard" + "RelationShard", + "DeferredErrorMessage" }; const char **CitusNodeTagNames = CitusNodeTagNamesD; @@ -383,6 +385,7 @@ const ExtensibleNodeMethods nodeMethods[] = DEFINE_NODE_METHODS(ShardPlacement), DEFINE_NODE_METHODS(RelationShard), DEFINE_NODE_METHODS(Task), + DEFINE_NODE_METHODS(DeferredErrorMessage), /* nodes with only output support */ DEFINE_NODE_METHODS_NO_READ(MultiNode), diff --git a/src/backend/distributed/utils/citus_outfuncs.c b/src/backend/distributed/utils/citus_outfuncs.c index ecb4150ba..28b943848 100644 --- a/src/backend/distributed/utils/citus_outfuncs.c +++ b/src/backend/distributed/utils/citus_outfuncs.c @@ -22,6 +22,7 @@ #include "distributed/citus_nodefuncs.h" #include "distributed/citus_nodes.h" +#include "distributed/errormessage.h" #include "distributed/multi_logical_planner.h" #include "distributed/multi_physical_planner.h" #include "distributed/multi_planner.h" @@ -515,6 +516,23 @@ OutTask(OUTFUNC_ARGS) WRITE_NODE_FIELD(relationShardList); } + +void +OutDeferredErrorMessage(OUTFUNC_ARGS) +{ + WRITE_LOCALS(DeferredErrorMessage); + WRITE_NODE_TYPE("DEFERREDERRORMESSAGE"); + + WRITE_INT_FIELD(code); + WRITE_STRING_FIELD(message); + WRITE_STRING_FIELD(detail); + WRITE_STRING_FIELD(hint); + WRITE_STRING_FIELD(filename); + WRITE_INT_FIELD(linenumber); + WRITE_STRING_FIELD(functionname); +} + + #if (PG_VERSION_NUM < 90600) /* @@ -635,6 +653,12 @@ outNode(StringInfo str, const void *obj) appendStringInfoChar(str, '}'); break; + case T_DeferredErrorMessage: + appendStringInfoChar(str, '{'); + OutDeferredErrorMessage(str, obj); + appendStringInfoChar(str, '}'); + break; + default: /* fall back into postgres' normal nodeToString machinery */ appendStringInfoString(str, nodeToString(obj)); diff --git a/src/backend/distributed/utils/citus_readfuncs.c b/src/backend/distributed/utils/citus_readfuncs.c index c430df0cb..149ae0492 100644 --- a/src/backend/distributed/utils/citus_readfuncs.c +++ b/src/backend/distributed/utils/citus_readfuncs.c @@ -14,6 +14,7 @@ #include #include "distributed/citus_nodefuncs.h" +#include "distributed/errormessage.h" #include "distributed/multi_planner.h" #include "nodes/parsenodes.h" #include "nodes/readfuncs.h" @@ -314,6 +315,24 @@ ReadTask(READFUNC_ARGS) READ_DONE(); } + +READFUNC_RET +ReadDeferredErrorMessage(READFUNC_ARGS) +{ + READ_LOCALS(DeferredErrorMessage); + + READ_INT_FIELD(code); + READ_STRING_FIELD(message); + READ_STRING_FIELD(detail); + READ_STRING_FIELD(hint); + READ_STRING_FIELD(filename); + READ_INT_FIELD(linenumber); + READ_STRING_FIELD(functionname); + + READ_DONE(); +} + + READFUNC_RET ReadUnsupportedCitusNode(READFUNC_ARGS) { diff --git a/src/backend/distributed/utils/citus_readfuncs_95.c b/src/backend/distributed/utils/citus_readfuncs_95.c index b5ac97576..b9719aeff 100644 --- a/src/backend/distributed/utils/citus_readfuncs_95.c +++ b/src/backend/distributed/utils/citus_readfuncs_95.c @@ -1519,6 +1519,8 @@ CitusParseNodeString(void) return_value = ReadRelationShard(); else if (MATCH("TASK", 4)) return_value = ReadTask(); + else if (MATCH("DEFERREDERRORMESSAGE", 20)) + return_value = ReadDeferredErrorMessage(); /* XXX: END Citus Nodes */ else { diff --git a/src/backend/distributed/utils/errormessage.c b/src/backend/distributed/utils/errormessage.c new file mode 100644 index 000000000..2482768e7 --- /dev/null +++ b/src/backend/distributed/utils/errormessage.c @@ -0,0 +1,58 @@ +/* + * errormessage.c + * Error handling related support functionality. + * + * Copyright (c) 2017, Citus Data, Inc. + */ + +#include "postgres.h" + +#include "distributed/citus_nodes.h" +#include "distributed/errormessage.h" + + +/* + * DeferredErrorInternal is a helper function for DeferredError(). + */ +DeferredErrorMessage * +DeferredErrorInternal(int code, const char *message, const char *detail, const char *hint, + const char *filename, int linenumber, const char *functionname) +{ + DeferredErrorMessage *error = CitusMakeNode(DeferredErrorMessage); + + error->code = code; + error->message = message; + error->detail = detail; + error->hint = hint; + error->filename = filename; + error->linenumber = linenumber; + error->functionname = functionname; + return error; +} + + +/* + * RaiseDeferredErrorInternal is a helper function for RaiseDeferredError(). + */ +void +RaiseDeferredErrorInternal(DeferredErrorMessage *error, int elevel) +{ + ErrorData *errorData = palloc0(sizeof(ErrorData)); + + errorData->sqlerrcode = error->code; + errorData->elevel = elevel; + errorData->message = pstrdup(error->message); + if (error->detail) + { + errorData->detail = pstrdup(error->detail); + } + if (error->hint) + { + errorData->hint = pstrdup(error->hint); + } + errorData->filename = pstrdup(error->filename); + errorData->lineno = error->linenumber; + errorData->funcname = error->functionname; + + ThrowErrorData(errorData); +} diff --git a/src/include/distributed/citus_nodefuncs.h b/src/include/distributed/citus_nodefuncs.h index 8d5c782d4..884e61c6a 100644 --- a/src/include/distributed/citus_nodefuncs.h +++ b/src/include/distributed/citus_nodefuncs.h @@ -68,6 +68,7 @@ extern READFUNC_RET ReadMapMergeJob(READFUNC_ARGS); extern READFUNC_RET ReadShardPlacement(READFUNC_ARGS); extern READFUNC_RET ReadRelationShard(READFUNC_ARGS); extern READFUNC_RET ReadTask(READFUNC_ARGS); +extern READFUNC_RET ReadDeferredErrorMessage(READFUNC_ARGS); extern READFUNC_RET ReadUnsupportedCitusNode(READFUNC_ARGS); @@ -78,6 +79,7 @@ extern void OutMapMergeJob(OUTFUNC_ARGS); extern void OutShardPlacement(OUTFUNC_ARGS); extern void OutRelationShard(OUTFUNC_ARGS); extern void OutTask(OUTFUNC_ARGS); +extern void OutDeferredErrorMessage(OUTFUNC_ARGS); extern void OutMultiNode(OUTFUNC_ARGS); extern void OutMultiTreeRoot(OUTFUNC_ARGS); diff --git a/src/include/distributed/citus_nodes.h b/src/include/distributed/citus_nodes.h index 05e7e0bd1..3a401c381 100644 --- a/src/include/distributed/citus_nodes.h +++ b/src/include/distributed/citus_nodes.h @@ -56,7 +56,8 @@ typedef enum CitusNodeTag T_Task, T_ShardInterval, T_ShardPlacement, - T_RelationShard + T_RelationShard, + T_DeferredErrorMessage } CitusNodeTag; @@ -99,6 +100,8 @@ CitusNodeTagI(Node *node) #else +#include "nodes/nodes.h" + typedef CitusNodeTag CitusNode; /* * nodeTag equivalent that returns the node tag for both citus and postgres diff --git a/src/include/distributed/errormessage.h b/src/include/distributed/errormessage.h new file mode 100644 index 000000000..26f8dd63a --- /dev/null +++ b/src/include/distributed/errormessage.h @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------- + * + * errormessage.h + * Error handling related support functionality. + * + * Copyright (c) 2017, Citus Data, Inc. + *------------------------------------------------------------------------- + */ + +#ifndef ERRORMESSAGE_H +#define ERRORMESSAGE_H + + +#include "distributed/citus_nodes.h" + + +typedef struct DeferredErrorMessage +{ + CitusNode tag; + + int code; + const char *message; + const char *detail; + const char *hint; + const char *filename; + int linenumber; + const char *functionname; +} DeferredErrorMessage; + + +/* + * DeferredError allocates a deferred error message, that can later be emitted + * using RaiseDeferredError(). These error messages can be + * serialized/copied/deserialized, i.e. can be embedded in plans and such. + */ +#define DeferredError(code, message, detail, hint) \ + DeferredErrorInternal(code, message, detail, hint, __FILE__, __LINE__, __func__) + +DeferredErrorMessage * DeferredErrorInternal(int code, const char *message, const + char *detail, const char *hint, + const char *filename, int linenumber, const + char *functionname); + +/* + * RaiseDeferredError emits a previously allocated error using the specified + * severity. + * + * The trickery with __builtin_constant_p/pg_unreachable aims to have the + * compiler understand that the function will not return if elevel >= ERROR. + */ +#define RaiseDeferredError(error, elevel) \ + do { \ + RaiseDeferredErrorInternal(error, elevel); \ + if (__builtin_constant_p(elevel) && (elevel) >= ERROR) { \ + pg_unreachable(); } \ + } while (0) + +void RaiseDeferredErrorInternal(DeferredErrorMessage *error, int elevel); + +#endif