Skip to content

Commit

Permalink
feat(c,format,python): implement error details
Browse files Browse the repository at this point in the history
  • Loading branch information
lidavidm committed Aug 2, 2023
1 parent 65d785d commit 5241ca1
Show file tree
Hide file tree
Showing 21 changed files with 835 additions and 61 deletions.
117 changes: 116 additions & 1 deletion adbc.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,25 @@ typedef uint8_t AdbcStatusCode;
/// May indicate a database-side error only.
#define ADBC_STATUS_UNAUTHORIZED 14

/// \brief Inform the driver/driver manager that we are using the extended
/// AdbcError struct from ADBC 1.1.0.
///
/// See the AdbcError documentation for usage.
///
/// \since ADBC API revision 1.1.0
/// \addtogroup adbc-1.1.0
#define ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA INT32_MIN

/// \brief A detailed error message for an operation.
///
/// The caller must zero-initialize this struct (clarified in ADBC 1.1.0).
///
/// The structure was extended in ADBC 1.1.0. Drivers and clients using ADBC
/// 1.0.0 will not have the private_data or private_driver fields. Drivers
/// should read/write these fields if and only if vendor_code is equal to
/// ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA. Clients are required to initialize
/// this struct to avoid the possibility of uninitialized values confusing the
/// driver.
struct ADBC_EXPORT AdbcError {
/// \brief The error message.
char* message;
Expand All @@ -266,8 +284,101 @@ struct ADBC_EXPORT AdbcError {
/// Unlike other structures, this is an embedded callback to make it
/// easier for the driver manager and driver to cooperate.
void (*release)(struct AdbcError* error);

/// \brief Opaque implementation-defined state.
///
/// This field may not be used unless vendor_code is
/// ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA. If present, this field is NULLPTR
/// iff the error is unintialized/freed.
///
/// \since ADBC API revision 1.1.0
/// \addtogroup adbc-1.1.0
void* private_data;

/// \brief The associated driver (used by the driver manager to help
/// track state).
///
/// This field may not be used unless vendor_code is
/// ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA.
///
/// \since ADBC API revision 1.1.0
/// \addtogroup adbc-1.1.0
struct AdbcDriver* private_driver;
};

#ifdef __cplusplus
/// \brief A helper to initialize the full AdbcError structure.
///
/// \since ADBC API revision 1.1.0
/// \addtogroup adbc-1.1.0
#define ADBC_ERROR_INIT \
(AdbcError{nullptr, \
ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA, \
{0, 0, 0, 0, 0}, \
nullptr, \
nullptr, \
nullptr})
#else
/// \brief A helper to initialize the full AdbcError structure.
///
/// \since ADBC API revision 1.1.0
/// \addtogroup adbc-1.1.0
#define ADBC_ERROR_INIT \
((struct AdbcError){ \
NULL, ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA, {0, 0, 0, 0, 0}, NULL, NULL, NULL})
#endif

/// \brief The size of the AdbcError structure in ADBC 1.0.0.
///
/// Drivers written for ADBC 1.1.0 and later should never touch more than this
/// portion of an AdbcDriver struct when vendor_code is not
/// ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA.
///
/// \since ADBC API revision 1.1.0
/// \addtogroup adbc-1.1.0
#define ADBC_ERROR_1_0_0_SIZE (offsetof(struct AdbcError, private_data))
/// \brief The size of the AdbcError structure in ADBC 1.1.0.
///
/// Drivers written for ADBC 1.1.0 and later should never touch more than this
/// portion of an AdbcDriver struct when vendor_code is
/// ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA.
///
/// \since ADBC API revision 1.1.0
/// \addtogroup adbc-1.1.0
#define ADBC_ERROR_1_1_0_SIZE (sizeof(struct AdbcError))

/// \brief Extra key-value metadata for an error.
///
/// The fields here are owned by the driver and should not be freed. The
/// fields here are invalidated when the release callback in AdbcError is
/// called.
///
/// \since ADBC API revision 1.1.0
/// \addtogroup adbc-1.1.0
struct ADBC_EXPORT AdbcErrorDetail {
/// \brief The metadata key.
const char* key;
/// \brief The binary metadata value.
const uint8_t* value;
/// \brief The length of the metadata value.
size_t value_length;
};

/// \brief Get the number of metadata values available in an error.
///
/// \since ADBC API revision 1.1.0
/// \addtogroup adbc-1.1.0
int AdbcErrorGetDetailCount(struct AdbcError* error);

/// \brief Get a metadata value in an error by index.
///
/// If index is invalid, returns an AdbcErrorDetail initialized with NULL/0
/// fields.
///
/// \since ADBC API revision 1.1.0
/// \addtogroup adbc-1.1.0
struct AdbcErrorDetail AdbcErrorGetDetail(struct AdbcError* error, int index);

/// @}

/// \defgroup adbc-constants Constants
Expand All @@ -284,6 +395,7 @@ struct ADBC_EXPORT AdbcError {
/// When passed to an AdbcDriverInitFunc(), the driver parameter must
/// point to an AdbcDriver.
///
/// \since ADBC API revision 1.1.0
/// \addtogroup adbc-1.1.0
#define ADBC_VERSION_1_1_0 1001000

Expand Down Expand Up @@ -894,6 +1006,9 @@ struct ADBC_EXPORT AdbcDriver {
///
/// @{

int (*ErrorGetDetailCount)(struct AdbcError* error);
struct AdbcErrorDetail (*ErrorGetDetail)(struct AdbcError* error, int index);

AdbcStatusCode (*DatabaseGetOption)(struct AdbcDatabase*, const char*, char*, size_t*,
struct AdbcError*);
AdbcStatusCode (*DatabaseGetOptionBytes)(struct AdbcDatabase*, const char*, uint8_t*,
Expand Down Expand Up @@ -959,7 +1074,7 @@ struct ADBC_EXPORT AdbcDriver {
///
/// \since ADBC API revision 1.1.0
/// \addtogroup adbc-1.1.0
#define ADBC_DRIVER_1_0_0_SIZE (offsetof(struct AdbcDriver, DatabaseGetOption))
#define ADBC_DRIVER_1_0_0_SIZE (offsetof(struct AdbcDriver, ErrorGetDetailCount))

/// \brief The size of the AdbcDriver structure in ADBC 1.1.0.
/// Drivers written for ADBC 1.1.0 and later should never touch more
Expand Down
152 changes: 145 additions & 7 deletions c/driver/common/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,174 @@
#include "utils.h"

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <assert.h>
#include "adbc.h"

static size_t kErrorBufferSize = 256;

/// For ADBC 1.1.0, the structure held in private_data.
struct AdbcErrorDetails {
char* message;

// The metadata keys (may be NULL).
char** keys;
// The metadata values (may be NULL).
uint8_t** values;
// The metadata value lengths (may be NULL).
size_t* lengths;
// The number of initialized metadata.
int count;
// The length of the keys/values/lengths arrays above.
int capacity;
};

static void ReleaseErrorWithDetails(struct AdbcError* error) {
struct AdbcErrorDetails* details = (struct AdbcErrorDetails*)error->private_data;
free(details->message);

for (int i = 0; i < details->count; i++) {
free(details->keys[i]);
free(details->values[i]);
}

free(details->keys);
free(details->values);
free(details->lengths);
free(error->private_data);
*error = ADBC_ERROR_INIT;
}

static void ReleaseError(struct AdbcError* error) {
free(error->message);
error->message = NULL;
error->release = NULL;
}

void SetError(struct AdbcError* error, const char* format, ...) {
va_list args;
va_start(args, format);
SetErrorVariadic(error, format, args);
va_end(args);
}

void SetErrorVariadic(struct AdbcError* error, const char* format, va_list args) {
if (!error) return;
if (error->release) {
// TODO: combine the errors if possible
error->release(error);
}
error->message = malloc(kErrorBufferSize);
if (!error->message) return;

error->release = &ReleaseError;
if (error->vendor_code == ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA) {
error->private_data = malloc(sizeof(struct AdbcErrorDetails));
if (!error->private_data) return;

struct AdbcErrorDetails* details = (struct AdbcErrorDetails*)error->private_data;

details->message = malloc(kErrorBufferSize);
if (!details->message) {
free(details);
return;
}
details->keys = NULL;
details->values = NULL;
details->lengths = NULL;
details->count = 0;
details->capacity = 0;

error->message = details->message;
error->release = &ReleaseErrorWithDetails;
} else {
error->message = malloc(kErrorBufferSize);
if (!error->message) return;

error->release = &ReleaseError;
}

va_list args;
va_start(args, format);
vsnprintf(error->message, kErrorBufferSize, format, args);
va_end(args);
}

void AppendErrorDetail(struct AdbcError* error, const char* key, const uint8_t* detail,
size_t detail_length) {
if (error->release != ReleaseErrorWithDetails) return;

struct AdbcErrorDetails* details = (struct AdbcErrorDetails*)error->private_data;
if (details->count >= details->capacity) {
int new_capacity = (details->capacity == 0) ? 4 : (2 * details->capacity);
char** new_keys = calloc(new_capacity, sizeof(char*));
if (!new_keys) {
return;
}

uint8_t** new_values = calloc(new_capacity, sizeof(uint8_t*));
if (!new_values) {
free(new_keys);
return;
}

size_t* new_lengths = calloc(new_capacity, sizeof(size_t*));
if (!new_lengths) {
free(new_keys);
free(new_values);
return;
}

memcpy(new_keys, details->keys, sizeof(char*) * details->count);
free(details->keys);
details->keys = new_keys;

memcpy(new_values, details->values, sizeof(uint8_t*) * details->count);
free(details->values);
details->values = new_values;

memcpy(new_lengths, details->lengths, sizeof(size_t) * details->count);
free(details->lengths);
details->lengths = new_lengths;

details->capacity = new_capacity;
}

char* key_data = strdup(key);
if (!key_data) return;
uint8_t* value_data = malloc(detail_length);
if (!value_data) {
free(key_data);
return;
}
memcpy(value_data, detail, detail_length);

int index = details->count;
details->keys[index] = key_data;
details->values[index] = value_data;
details->lengths[index] = detail_length;

details->count++;
}

int CommonErrorGetDetailCount(struct AdbcError* error) {
if (error->release != ReleaseErrorWithDetails) {
return 0;
}
struct AdbcErrorDetails* details = (struct AdbcErrorDetails*)error->private_data;
return details->count;
}

struct AdbcErrorDetail CommonErrorGetDetail(struct AdbcError* error, int index) {
if (error->release != ReleaseErrorWithDetails) {
return (struct AdbcErrorDetail){NULL, NULL, 0};
}
struct AdbcErrorDetails* details = (struct AdbcErrorDetails*)error->private_data;
if (index < 0 || index >= details->count) {
return (struct AdbcErrorDetail){NULL, NULL, 0};
}
return (struct AdbcErrorDetail){
.key = details->keys[index],
.value = details->values[index],
.value_length = details->lengths[index],
};
}

struct SingleBatchArrayStream {
Expand Down
13 changes: 12 additions & 1 deletion c/driver/common/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#pragma once

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>

Expand All @@ -35,10 +36,20 @@ extern "C" {
#define ADBC_CHECK_PRINTF_ATTRIBUTE
#endif

/// Set error details using a format string.
/// Set error message using a format string.
void SetError(struct AdbcError* error, const char* format,
...) ADBC_CHECK_PRINTF_ATTRIBUTE;

/// Set error message using a format string.
void SetErrorVariadic(struct AdbcError* error, const char* format, va_list args);

/// Add an error detail.
void AppendErrorDetail(struct AdbcError* error, const char* key, const uint8_t* detail,
size_t detail_length);

int CommonErrorGetDetailCount(struct AdbcError* error);
struct AdbcErrorDetail CommonErrorGetDetail(struct AdbcError* error, int index);

struct StringBuilder {
char* buffer;
// Not including null terminator
Expand Down
Loading

0 comments on commit 5241ca1

Please sign in to comment.