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.
pull/1134/head
Andres Freund 2017-01-19 16:45:37 -08:00
parent 9a82e8f06b
commit 557ccc6fda
8 changed files with 173 additions and 2 deletions

View File

@ -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),

View File

@ -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));

View File

@ -14,6 +14,7 @@
#include <math.h>
#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)
{

View File

@ -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
{

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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