The NULL values were not stored as NULL Avro values due to the fact that the file format has no native NULL-ness for the basic types. To solve this, all values must be stored as a union that contains the actual type as well as the null type. Unions were not implemented in the maxavro library but implementing means simply recursing one level down.
224 lines
5.7 KiB
C
224 lines
5.7 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/bsl11.
|
|
*
|
|
* Change Date: 2020-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_internal.h"
|
|
#include <jansson.h>
|
|
#include <string.h>
|
|
#include <maxscale/debug.h>
|
|
#include <maxscale/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_array(object) && json_is_object(json_array_get(object, 0)))
|
|
{
|
|
json_incref(object);
|
|
field->extra = object;
|
|
return MAXAVRO_TYPE_UNION;
|
|
}
|
|
|
|
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++)
|
|
{
|
|
MXS_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)
|
|
{
|
|
MXS_FREE(rval);
|
|
rval = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MXS_ERROR("Memory allocation failed.");
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
static void maxavro_schema_field_free(MAXAVRO_SCHEMA_FIELD *field)
|
|
{
|
|
if (field)
|
|
{
|
|
MXS_FREE(field->name);
|
|
if (field->type == MAXAVRO_TYPE_ENUM || field->type == MAXAVRO_TYPE_UNION)
|
|
{
|
|
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]);
|
|
}
|
|
MXS_FREE(schema->fields);
|
|
MXS_FREE(schema);
|
|
}
|
|
}
|