217 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			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);
 | 
						|
    }
 | 
						|
}
 |