Skip to content

Commit

Permalink
Feature CORE-832 - Parameterized exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
asfernandes committed Jan 7, 2010
1 parent d94f708 commit be85998
Show file tree
Hide file tree
Showing 21 changed files with 215 additions and 92 deletions.
40 changes: 40 additions & 0 deletions doc/sql.extensions/README.exception_handling
Expand Up @@ -75,3 +75,43 @@ Run-time error codes (FB 1.5)

See also:
README.context_variables


Parameterized exceptions (FB 3.0)
---------------------------------

Function:
Allow to define exception message with slots for parameters and pass the parameters when
raising the exception.

Author:
Adriano dos Santos Fernandes <adrianosf@gmail.com>

Syntax:
EXCEPTION <name> USING ( <value list> )

Example:
create exception e_invalid_val 'Invalid value @1 for the field @2';

...
if (val < 1000) then
thing = val;
else
exception e_invalid_val using (val, 'thing');
end

Notes:
The status vector is generated using that codes combination:
isc_except, <exception number>,
isc_formatted_exception, <exception message>, <exception parameters>

The client library formats isc_formatted_exception in an special way, using the exception
parameters as argument to the exception message, instead of formatting all arguments of
isc_formatted_exception for a predefined internal text. That's important for any code that
formats status vectors without the help of the client library. Also, the client library older
than 3.0 will not be able to correctly format exceptions raised in this new way.

@N means the N parameter (where N starts at 1) passed in the exception raise command. If a N
parameter is not passed, the text is not substituted. If NULL is passed, it's replaced by string
'*** null ***'. If more parameters is passed than used in the exception message, they are
ignored.
2 changes: 2 additions & 0 deletions lang_helpers/gds_codes.ftn
Expand Up @@ -1442,6 +1442,8 @@ C --
PARAMETER (GDS__invalid_timestamp_val = 335545014)
INTEGER*4 GDS__invalid_index_val
PARAMETER (GDS__invalid_index_val = 335545015)
INTEGER*4 GDS__formatted_exception
PARAMETER (GDS__formatted_exception = 335545016)
INTEGER*4 GDS__gfix_db_name
PARAMETER (GDS__gfix_db_name = 335740929)
INTEGER*4 GDS__gfix_invalid_sw
Expand Down
1 change: 1 addition & 0 deletions lang_helpers/gds_codes.pas
Expand Up @@ -728,6 +728,7 @@
gds_invalid_time_val = 335545013;
gds_invalid_timestamp_val = 335545014;
gds_invalid_index_val = 335545015;
gds_formatted_exception = 335545016;
gds_gfix_db_name = 335740929;
gds_gfix_invalid_sw = 335740930;
gds_gfix_incmp_sw = 335740932;
Expand Down
85 changes: 47 additions & 38 deletions src/common/classes/MsgPrint.cpp
Expand Up @@ -28,10 +28,12 @@
#include "../jrd/common.h"
#include "BaseStream.h"
#include "MsgPrint.h"
#include "array.h"
#include <string.h>
#include "../jrd/gds_proto.h"
#include "../common/utils_proto.h"
#include "../jrd/file_params.h"
#include "../jrd/msg_encode.h"


namespace MsgFormat
Expand Down Expand Up @@ -233,7 +235,7 @@ int MsgPrintHelper(BaseStream& out_stream, const safe_cell& item)


// Prints the whole chain of arguments, according to format and in the specified stream.
int MsgPrint(BaseStream& out_stream, const char* format, const SafeArg& arg)
int MsgPrint(BaseStream& out_stream, const char* format, const SafeArg& arg, bool userFormatting)
{
int out_bytes = 0;
for (const char* iter = format; true; ++iter)
Expand All @@ -247,25 +249,37 @@ int MsgPrint(BaseStream& out_stream, const char* format, const SafeArg& arg)
switch (iter[1])
{
case 0:
out_bytes += out_stream.write("@(EOF)", 6);
if (userFormatting)
out_bytes += out_stream.write("@", 1);
else
out_bytes += out_stream.write("@(EOF)", 6);
return out_bytes;
case '@':
out_bytes += out_stream.write(iter, 1);
break;
default:
{
const int pos = iter[1] - '0';
if (pos > 0 && static_cast<size_t>(pos) <= arg.m_count)
const int start = userFormatting ? 1 : 0;
const int pos = start + iter[1] - '0';
if (pos > start && static_cast<size_t>(pos) <= arg.m_count)
out_bytes += MsgPrintHelper(out_stream, arg.m_arguments[pos - 1]);
else if (pos >= 0 && pos <= 9)
else
{
// Show the missing or out of range param number.
out_bytes += MsgPrint(out_stream,
"<Missing arg #@1 - possibly status vector overflow>",
SafeArg() << pos);
if (userFormatting)
{
out_bytes += out_stream.write("@", 1);
out_bytes += out_stream.write(iter + 1, 1);
}
else if (pos >= 0 && pos <= 9)
{
// Show the missing or out of range param number.
out_bytes += MsgPrint(out_stream,
"<Missing arg #@1 - possibly status vector overflow>",
SafeArg() << pos);
}
else // Something not a number following @, invalid.
out_bytes += out_stream.write("(error)", 7);
}
else // Something not a number following @, invalid.
out_bytes += out_stream.write("(error)", 7);
}
}
++iter;
Expand Down Expand Up @@ -308,10 +322,10 @@ int MsgPrint(BaseStream& out_stream, const char* format, const SafeArg& arg)


// Shortcut version to format a string with arguments on standard output.
int MsgPrint(const char* format, const SafeArg& arg)
int MsgPrint(const char* format, const SafeArg& arg, bool userFormatting)
{
StdioStream st(stdout);
return MsgPrint(st, format, arg);
return MsgPrint(st, format, arg, userFormatting);
}


Expand All @@ -327,44 +341,28 @@ int MsgPrint(const char* format)

// Shortcut version to format a string with arguments on a string output
// of a given size.
int MsgPrint(char* plainstring, unsigned int s_size, const char* format, const SafeArg& arg)
int MsgPrint(char* plainstring, unsigned int s_size, const char* format, const SafeArg& arg,
bool userFormatting)
{
StringStream st(plainstring, s_size);
return MsgPrint(st, format, arg);
return MsgPrint(st, format, arg, userFormatting);
}


// Shortcut version to format a string with arguments on standard error.
int MsgPrintErr(const char* format, const SafeArg& arg)
int MsgPrintErr(const char* format, const SafeArg& arg, bool userFormatting)
{
StdioStream st(stderr, true); // flush
return MsgPrint(st, format, arg);
return MsgPrint(st, format, arg, userFormatting);
}

} // namespace


int fb_msg_format(void* handle,
USHORT facility,
USHORT number,
unsigned int bsize,
TEXT* buffer,
const MsgFormat::SafeArg& arg)
// Lookup and format message. Return as much of formatted string as fits in caller's buffer.
int fb_msg_format(void* handle, USHORT facility, USHORT number, unsigned int bsize, TEXT* buffer,
const MsgFormat::SafeArg& arg)
{
/**************************************
*
* f b _ m s g _ f o r m a t
*
**************************************
*
* Functional description
* Lookup and format message. Return as much of formatted string
* as fits in caller's buffer.
*
**************************************/

using MsgFormat::MsgPrint;

// The field MESSAGES.TEXT is 118 bytes long.
int total_msg = 0;
char msg[120] = "";
Expand All @@ -380,7 +378,18 @@ int fb_msg_format(void* handle,
total_msg = fb_utils::snprintf(buffer, bsize, msg, rep[0], rep[1], rep[2], rep[3], rep[4]);
}
else
total_msg = MsgPrint(buffer, bsize, msg, arg);
{
if (ENCODE_ISC_MSG(number, facility) == isc_formatted_exception && arg.getCount() > 0)
{
Firebird::HalfStaticArray<char, BUFFER_SMALL> msgBuffer(bsize);
MsgFormat::StringStream msgStream(msgBuffer.begin(), bsize);
MsgPrintHelper(msgStream, arg.getCell(0));

total_msg = MsgPrint(buffer, bsize, msgBuffer.begin(), arg, 1);
}
else
total_msg = MsgPrint(buffer, bsize, msg, arg);
}
}
else
{
Expand Down
10 changes: 6 additions & 4 deletions src/common/classes/MsgPrint.h
Expand Up @@ -80,19 +80,21 @@ class BaseStream;


// A. The basic routine.
int MsgPrint(BaseStream& out_stream, const char* format, const SafeArg& arg);
int MsgPrint(BaseStream& out_stream, const char* format, const SafeArg& arg,
bool userFormatting = false);

// B. Printf replacement. Output to stdout.
int MsgPrint(const char* format, const SafeArg& arg);
int MsgPrint(const char* format, const SafeArg& arg, bool userFormatting = false);

// C. Print without arguments to stdout.
int MsgPrint(const char* format);

// D. Print to a string, without buffer overrun.
int MsgPrint(char* plainstring, unsigned int s_size, const char* format, const SafeArg& arg);
int MsgPrint(char* plainstring, unsigned int s_size, const char* format, const SafeArg& arg,
bool userFormatting = false);

// E. Prints a formatted string into stderr and flushed the buffer.
int MsgPrintErr(const char* format, const SafeArg& arg);
int MsgPrintErr(const char* format, const SafeArg& arg, bool userFormatting = false);
} // namespace

// Type safe replacement of the old gds__msg_format.
Expand Down
5 changes: 4 additions & 1 deletion src/common/classes/SafeArg.h
Expand Up @@ -166,11 +166,14 @@ class SafeArg
void dump(const TEXT* target[], size_t v_size) const;
const safe_cell& getCell(size_t index) const;
size_t getCount() const;

private:
size_t m_count;
safe_cell m_arguments[SAFEARG_MAX_ARG];
const void* m_extras; // Formatting, etc.
friend int MsgPrint(BaseStream& out_stream, const char* format, const SafeArg& arg);

friend int MsgPrint(BaseStream& out_stream, const char* format, const SafeArg& arg,
bool userFormatting);
};

inline SafeArg::SafeArg()
Expand Down

0 comments on commit be85998

Please sign in to comment.