Files
MaxScale/avro/maxavro_schema.c
Markus Mäkelä 8da655b7cb Improve maxavro failure handling and error messages
When the creation of the Avro schema would fail for a file that is being
opened, the errors wouldn't be handled properly. Also free all allocated
memory on failure.

All errors that set errno are now properly logged with the error number
and message.
2017-01-26 16:16:41 +02:00

217 lines
5.4 KiB
C

/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl.
*
* Change Date: 2019-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include "maxavro.h"
#include <jansson.h>
#include <string.h>
#include <skygw_debug.h>
#include <log_manager.h>
static const MAXAVRO_SCHEMA_FIELD types[MAXAVRO_TYPE_MAX] =
{
{"int", NULL, MAXAVRO_TYPE_INT},
{"long", NULL, MAXAVRO_TYPE_LONG},
{"float", NULL, MAXAVRO_TYPE_FLOAT},
{"double", NULL, MAXAVRO_TYPE_DOUBLE},
{"bool", NULL, MAXAVRO_TYPE_BOOL},
{"bytes", NULL, MAXAVRO_TYPE_BYTES},
{"string", NULL, MAXAVRO_TYPE_STRING},
{"enum", NULL, MAXAVRO_TYPE_ENUM},
{"null", NULL, MAXAVRO_TYPE_NULL},
{NULL, NULL, MAXAVRO_TYPE_UNKNOWN}
};
/**
* @brief Convert string to Avro value type
*
* @param type Value string
* @return Avro value type
*/
enum maxavro_value_type string_to_type(const char *str)
{
for (int i = 0; types[i].name; i++)
{
if (strcmp(str, types[i].name) == 0)
{
return types[i].type;
}
}
return MAXAVRO_TYPE_UNKNOWN;
}
/**
* @brief Convert Avro value type to string
*
* @param type Type of the value
* @return String representation of the value
*/
const char* type_to_string(enum maxavro_value_type type)
{
for (int i = 0; types[i].name; i++)
{
if (types[i].type == type)
{
return types[i].name;
}
}
return "unknown type";
}
/**
* @brief extract the type definition from a JSON schema
* @param object JSON object containing the schema
* @param field The associated field
* @return Type of the field
*/
static enum maxavro_value_type unpack_to_type(json_t *object,
MAXAVRO_SCHEMA_FIELD* field)
{
enum maxavro_value_type rval = MAXAVRO_TYPE_UNKNOWN;
json_t* type = NULL;
if (json_is_object(object))
{
json_t *tmp = NULL;
json_unpack(object, "{s:o}", "type", &tmp);
type = tmp;
}
if (json_is_array(object))
{
json_t *tmp = json_array_get(object, 0);
type = tmp;
}
if (type && json_is_string(type))
{
const char *value = json_string_value(type);
rval = string_to_type(value);
if (rval == MAXAVRO_TYPE_ENUM)
{
json_t *tmp = NULL;
json_unpack(object, "{s:o}", "symbols", &tmp);
ss_dassert(json_is_array(tmp));
json_incref(tmp);
field->extra = tmp;
}
}
return rval;
}
/**
* @brief Create a new Avro schema from JSON
* @param json JSON from which the schema is created from
* @return New schema or NULL if an error occurred
*/
MAXAVRO_SCHEMA* maxavro_schema_alloc(const char* json)
{
MAXAVRO_SCHEMA* rval = malloc(sizeof(MAXAVRO_SCHEMA));
if (rval)
{
bool error = false;
json_error_t err;
json_t *schema = json_loads(json, 0, &err);
if (schema)
{
json_t *field_arr = NULL;
if (json_unpack(schema, "{s:o}", "fields", &field_arr) == 0)
{
size_t arr_size = json_array_size(field_arr);
rval->fields = malloc(sizeof(MAXAVRO_SCHEMA_FIELD) * arr_size);
rval->num_fields = arr_size;
for (int i = 0; i < arr_size; i++)
{
json_t *object = json_array_get(field_arr, i);
char *key;
json_t *value_obj;
if (object && json_unpack(object, "{s:s s:o}", "name", &key, "type", &value_obj) == 0)
{
rval->fields[i].name = strdup(key);
rval->fields[i].type = unpack_to_type(value_obj, &rval->fields[i]);
}
else
{
MXS_ERROR("Failed to unpack JSON Object \"name\": %s", json);
error = true;
for (int j = 0; j < i; j++)
{
free(rval->fields[j].name);
}
break;
}
}
}
else
{
MXS_ERROR("Failed to unpack JSON Object \"fields\": %s", json);
error = true;
}
json_decref(schema);
}
else
{
MXS_ERROR("Failed to read JSON schema: %s", json);
error = true;
}
if (error)
{
free(rval);
rval = NULL;
}
}
else
{
MXS_ERROR("Memory allocation failed.");
}
return rval;
}
static void maxavro_schema_field_free(MAXAVRO_SCHEMA_FIELD *field)
{
if (field)
{
free(field->name);
if (field->type == MAXAVRO_TYPE_ENUM)
{
json_decref((json_t*)field->extra);
}
}
}
/**
* Free a MAXAVRO_SCHEMA object
* @param schema Schema to free
*/
void maxavro_schema_free(MAXAVRO_SCHEMA* schema)
{
if (schema)
{
for (int i = 0; i < schema->num_fields; i++)
{
maxavro_schema_field_free(&schema->fields[i]);
}
free(schema->fields);
free(schema);
}
}