MaxScale/avro/maxavro_schema.c
2017-02-14 21:42:28 +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/bsl11.
*
* Change Date: 2019-07-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 <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_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);
}
}