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