mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 12:56:14 +01:00 
			
		
		
		
	I checked all stack overflow potential problems found with gcc -Wstack-usage=16384 and clang -Wframe-larger-than=16384 -no-inline Fixes: Added '#pragma clang diagnostic ignored "-Wframe-larger-than="' to a lot of function to where stack usage large but resonable. - Added stack check warnings to BUILD scrips when using clang and debug. Function changed to use malloc instead allocating things on stack: - read_bootstrap_query() now allocates line_buffer (20000 bytes) with malloc() instead of using stack. This has a small performance impact but this is not releant for bootstrap. - mroonga grn_select() used 65856 bytes on stack. Changed it to use malloc(). - Wsrep_schema::replay_transaction() and Wsrep_schema::recover_sr_transactions(). - Connect zipOpen3() Not fixed: - mroonga/vendor/groonga/lib/expr.c grn_proc_call() uses 43712 byte on stack. However this is not easy to fix as the stack used is caused by a lot of code generated by defines. - Most changes in mroonga/groonga where only adding of pragmas to disable stack warnings. - rocksdb/options/options_helper.cc uses 20288 of stack space. (no reason to fix except to get rid of the compiler warning) - Causes using alloca() where the allocation size is resonable. - An issue in libmariadb (reported to connectors).
		
			
				
	
	
		
			1235 lines
		
	
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1235 lines
		
	
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* -*- c-basic-offset: 2 -*- */
 | 
						|
/*
 | 
						|
  Copyright(C) 2009-2017 Brazil
 | 
						|
 | 
						|
  This library is free software; you can redistribute it and/or
 | 
						|
  modify it under the terms of the GNU Lesser General Public
 | 
						|
  License version 2.1 as published by the Free Software Foundation.
 | 
						|
 | 
						|
  This library is distributed in the hope that it will be useful,
 | 
						|
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
  Lesser General Public License for more details.
 | 
						|
 | 
						|
  You should have received a copy of the GNU Lesser General Public
 | 
						|
  License along with this library; if not, write to the Free Software
 | 
						|
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
 | 
						|
*/
 | 
						|
 | 
						|
#include "grn_load.h"
 | 
						|
#include "grn_ctx_impl.h"
 | 
						|
#include "grn_db.h"
 | 
						|
#include "grn_util.h"
 | 
						|
#include <my_attribute.h>
 | 
						|
 | 
						|
PRAGMA_DISABLE_CHECK_STACK_FRAME
 | 
						|
 | 
						|
static void
 | 
						|
grn_loader_save_error(grn_ctx *ctx, grn_loader *loader)
 | 
						|
{
 | 
						|
  loader->rc = ctx->rc;
 | 
						|
  grn_strcpy(loader->errbuf, GRN_CTX_MSGSIZE, ctx->errbuf);
 | 
						|
}
 | 
						|
 | 
						|
static grn_obj *
 | 
						|
values_add(grn_ctx *ctx, grn_loader *loader)
 | 
						|
{
 | 
						|
  grn_obj *res;
 | 
						|
  uint32_t curr_size = loader->values_size * sizeof(grn_obj);
 | 
						|
  if (curr_size < GRN_TEXT_LEN(&loader->values)) {
 | 
						|
    res = (grn_obj *)(GRN_TEXT_VALUE(&loader->values) + curr_size);
 | 
						|
    res->header.domain = GRN_DB_TEXT;
 | 
						|
    GRN_BULK_REWIND(res);
 | 
						|
  } else {
 | 
						|
    if (grn_bulk_space(ctx, &loader->values, sizeof(grn_obj))) { return NULL; }
 | 
						|
    res = (grn_obj *)(GRN_TEXT_VALUE(&loader->values) + curr_size);
 | 
						|
    GRN_TEXT_INIT(res, 0);
 | 
						|
  }
 | 
						|
  loader->values_size++;
 | 
						|
  loader->last = res;
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
static grn_obj *
 | 
						|
values_next(grn_ctx *ctx, grn_obj *value)
 | 
						|
{
 | 
						|
  if (value->header.domain == GRN_JSON_LOAD_OPEN_BRACKET ||
 | 
						|
      value->header.domain == GRN_JSON_LOAD_OPEN_BRACE) {
 | 
						|
    value += GRN_UINT32_VALUE(value);
 | 
						|
  }
 | 
						|
  return value + 1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
values_len(grn_ctx *ctx, grn_obj *head, grn_obj *tail)
 | 
						|
{
 | 
						|
  int len;
 | 
						|
  for (len = 0; head < tail; head = values_next(ctx, head), len++) ;
 | 
						|
  return len;
 | 
						|
}
 | 
						|
 | 
						|
static grn_id
 | 
						|
loader_add(grn_ctx *ctx, grn_obj *key)
 | 
						|
{
 | 
						|
  int added = 0;
 | 
						|
  grn_loader *loader = &ctx->impl->loader;
 | 
						|
  grn_id id = grn_table_add_by_key(ctx, loader->table, key, &added);
 | 
						|
  if (id == GRN_ID_NIL) {
 | 
						|
    grn_loader_save_error(ctx, loader);
 | 
						|
    return id;
 | 
						|
  }
 | 
						|
  if (!added && loader->ifexists) {
 | 
						|
    grn_obj *v = grn_expr_get_var_by_offset(ctx, loader->ifexists, 0);
 | 
						|
    grn_obj *result;
 | 
						|
    GRN_RECORD_SET(ctx, v, id);
 | 
						|
    result = grn_expr_exec(ctx, loader->ifexists, 0);
 | 
						|
    if (!grn_obj_is_true(ctx, result)) {
 | 
						|
      id = 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return id;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
add_weight_vector(grn_ctx *ctx,
 | 
						|
                  grn_obj *column,
 | 
						|
                  grn_obj *value,
 | 
						|
                  grn_obj *vector)
 | 
						|
{
 | 
						|
  unsigned int i, n;
 | 
						|
  grn_obj weight_buffer;
 | 
						|
 | 
						|
  n = GRN_UINT32_VALUE(value);
 | 
						|
  GRN_UINT32_INIT(&weight_buffer, 0);
 | 
						|
  for (i = 0; i < n; i += 2) {
 | 
						|
    grn_rc rc;
 | 
						|
    grn_obj *key, *weight;
 | 
						|
 | 
						|
    key = value + 1 + i;
 | 
						|
    weight = key + 1;
 | 
						|
 | 
						|
    GRN_BULK_REWIND(&weight_buffer);
 | 
						|
    rc = grn_obj_cast(ctx, weight, &weight_buffer, GRN_TRUE);
 | 
						|
    if (rc != GRN_SUCCESS) {
 | 
						|
      grn_obj *range;
 | 
						|
      range = grn_ctx_at(ctx, weight_buffer.header.domain);
 | 
						|
      ERR_CAST(column, range, weight);
 | 
						|
      grn_obj_unlink(ctx, range);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    grn_vector_add_element(ctx,
 | 
						|
                           vector,
 | 
						|
                           GRN_BULK_HEAD(key),
 | 
						|
                           GRN_BULK_VSIZE(key),
 | 
						|
                           GRN_UINT32_VALUE(&weight_buffer),
 | 
						|
                           key->header.domain);
 | 
						|
  }
 | 
						|
  GRN_OBJ_FIN(ctx, &weight_buffer);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
set_vector(grn_ctx *ctx, grn_obj *column, grn_id id, grn_obj *vector)
 | 
						|
{
 | 
						|
  int n = GRN_UINT32_VALUE(vector);
 | 
						|
  grn_obj buf, *v = vector + 1;
 | 
						|
  grn_id range_id;
 | 
						|
  grn_obj *range;
 | 
						|
 | 
						|
  range_id = DB_OBJ(column)->range;
 | 
						|
  range = grn_ctx_at(ctx, range_id);
 | 
						|
  if (grn_obj_is_table(ctx, range)) {
 | 
						|
    GRN_RECORD_INIT(&buf, GRN_OBJ_VECTOR, range_id);
 | 
						|
    while (n--) {
 | 
						|
      grn_bool cast_failed = GRN_FALSE;
 | 
						|
      grn_obj record, *element = v;
 | 
						|
      if (range_id != element->header.domain) {
 | 
						|
        GRN_RECORD_INIT(&record, 0, range_id);
 | 
						|
        if (grn_obj_cast(ctx, element, &record, GRN_TRUE)) {
 | 
						|
          cast_failed = GRN_TRUE;
 | 
						|
          ERR_CAST(column, range, element);
 | 
						|
        }
 | 
						|
        element = &record;
 | 
						|
      }
 | 
						|
      if (!cast_failed) {
 | 
						|
        GRN_UINT32_PUT(ctx, &buf, GRN_RECORD_VALUE(element));
 | 
						|
      }
 | 
						|
      if (element == &record) { GRN_OBJ_FIN(ctx, element); }
 | 
						|
      v = values_next(ctx, v);
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    if (((struct _grn_type *)range)->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) {
 | 
						|
      GRN_TEXT_INIT(&buf, GRN_OBJ_VECTOR);
 | 
						|
      while (n--) {
 | 
						|
        switch (v->header.domain) {
 | 
						|
        case GRN_DB_TEXT :
 | 
						|
        {
 | 
						|
          grn_bool cast_failed = GRN_FALSE;
 | 
						|
          grn_obj casted_element, *element = v;
 | 
						|
          if (range_id != element->header.domain) {
 | 
						|
            GRN_OBJ_INIT(&casted_element, GRN_BULK, 0, range_id);
 | 
						|
            if (grn_obj_cast(ctx, element, &casted_element, GRN_TRUE)) {
 | 
						|
              cast_failed = GRN_TRUE;
 | 
						|
              ERR_CAST(column, range, element);
 | 
						|
            }
 | 
						|
            element = &casted_element;
 | 
						|
          }
 | 
						|
          if (!cast_failed) {
 | 
						|
            grn_vector_add_element(ctx, &buf,
 | 
						|
                                   GRN_TEXT_VALUE(element),
 | 
						|
                                   GRN_TEXT_LEN(element),
 | 
						|
                                   0,
 | 
						|
                                   element->header.domain);
 | 
						|
          }
 | 
						|
          if (element == &casted_element) { GRN_OBJ_FIN(ctx, element); }
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        case GRN_JSON_LOAD_OPEN_BRACE :
 | 
						|
          add_weight_vector(ctx, column, v, &buf);
 | 
						|
          n -= GRN_UINT32_VALUE(v);
 | 
						|
          break;
 | 
						|
        default :
 | 
						|
          ERR(GRN_INVALID_ARGUMENT, "array must contain string or object");
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        v = values_next(ctx, v);
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      grn_id value_size = ((grn_db_obj *)range)->range;
 | 
						|
      GRN_VALUE_FIX_SIZE_INIT(&buf, GRN_OBJ_VECTOR, range_id);
 | 
						|
      while (n--) {
 | 
						|
        grn_bool cast_failed = GRN_FALSE;
 | 
						|
        grn_obj casted_element, *element = v;
 | 
						|
        if (range_id != element->header.domain) {
 | 
						|
          GRN_OBJ_INIT(&casted_element, GRN_BULK, 0, range_id);
 | 
						|
          if (grn_obj_cast(ctx, element, &casted_element, GRN_TRUE)) {
 | 
						|
            cast_failed = GRN_TRUE;
 | 
						|
            ERR_CAST(column, range, element);
 | 
						|
          }
 | 
						|
          element = &casted_element;
 | 
						|
        }
 | 
						|
        if (!cast_failed) {
 | 
						|
          grn_bulk_write(ctx, &buf, GRN_TEXT_VALUE(element), value_size);
 | 
						|
        }
 | 
						|
        if (element == &casted_element) { GRN_OBJ_FIN(ctx, element); }
 | 
						|
        v = values_next(ctx, v);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  grn_obj_set_value(ctx, column, id, &buf, GRN_OBJ_SET);
 | 
						|
  GRN_OBJ_FIN(ctx, &buf);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
set_weight_vector(grn_ctx *ctx, grn_obj *column, grn_id id, grn_obj *value)
 | 
						|
{
 | 
						|
  if (!grn_obj_is_weight_vector_column(ctx, column)) {
 | 
						|
    char column_name[GRN_TABLE_MAX_KEY_SIZE];
 | 
						|
    int column_name_size;
 | 
						|
    column_name_size = grn_obj_name(ctx, column, column_name,
 | 
						|
                                    GRN_TABLE_MAX_KEY_SIZE);
 | 
						|
    ERR(GRN_INVALID_ARGUMENT,
 | 
						|
        "<%.*s>: columns except weight vector column don't support object value",
 | 
						|
        column_name_size, column_name);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  {
 | 
						|
    grn_obj vector;
 | 
						|
 | 
						|
    GRN_TEXT_INIT(&vector, GRN_OBJ_VECTOR);
 | 
						|
    add_weight_vector(ctx, column, value, &vector);
 | 
						|
    grn_obj_set_value(ctx, column, id, &vector, GRN_OBJ_SET);
 | 
						|
    GRN_OBJ_FIN(ctx, &vector);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static inline int
 | 
						|
name_equal(const char *p, unsigned int size, const char *name)
 | 
						|
{
 | 
						|
  if (strlen(name) != size) { return 0; }
 | 
						|
  if (*p != GRN_DB_PSEUDO_COLUMN_PREFIX) { return 0; }
 | 
						|
  return !memcmp(p + 1, name + 1, size - 1);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
report_set_column_value_failure(grn_ctx *ctx,
 | 
						|
                                grn_obj *key,
 | 
						|
                                const char *column_name,
 | 
						|
                                unsigned int column_name_size,
 | 
						|
                                grn_obj *column_value)
 | 
						|
{
 | 
						|
  grn_obj key_inspected, column_value_inspected;
 | 
						|
 | 
						|
  GRN_TEXT_INIT(&key_inspected, 0);
 | 
						|
  GRN_TEXT_INIT(&column_value_inspected, 0);
 | 
						|
  grn_inspect_limited(ctx, &key_inspected, key);
 | 
						|
  grn_inspect_limited(ctx, &column_value_inspected, column_value);
 | 
						|
  GRN_LOG(ctx, GRN_LOG_ERROR,
 | 
						|
          "[table][load] failed to set column value: %s: "
 | 
						|
          "key: <%.*s>, column: <%.*s>, value: <%.*s>",
 | 
						|
          ctx->errbuf,
 | 
						|
          (int)GRN_TEXT_LEN(&key_inspected),
 | 
						|
          GRN_TEXT_VALUE(&key_inspected),
 | 
						|
          column_name_size,
 | 
						|
          column_name,
 | 
						|
          (int)GRN_TEXT_LEN(&column_value_inspected),
 | 
						|
          GRN_TEXT_VALUE(&column_value_inspected));
 | 
						|
  GRN_OBJ_FIN(ctx, &key_inspected);
 | 
						|
  GRN_OBJ_FIN(ctx, &column_value_inspected);
 | 
						|
}
 | 
						|
 | 
						|
static grn_id
 | 
						|
parse_id_value(grn_ctx *ctx, grn_obj *value)
 | 
						|
{
 | 
						|
  switch (value->header.type) {
 | 
						|
  case GRN_DB_UINT32 :
 | 
						|
    return GRN_UINT32_VALUE(value);
 | 
						|
  case GRN_DB_INT32 :
 | 
						|
    return GRN_INT32_VALUE(value);
 | 
						|
  default :
 | 
						|
    {
 | 
						|
      grn_id id = GRN_ID_NIL;
 | 
						|
      grn_obj casted_value;
 | 
						|
      GRN_UINT32_INIT(&casted_value, 0);
 | 
						|
      if (grn_obj_cast(ctx, value, &casted_value, GRN_FALSE) != GRN_SUCCESS) {
 | 
						|
        grn_obj inspected;
 | 
						|
        GRN_TEXT_INIT(&inspected, 0);
 | 
						|
        grn_inspect(ctx, &inspected, value);
 | 
						|
        ERR(GRN_INVALID_ARGUMENT,
 | 
						|
            "<%s>: failed to cast to <UInt32>: <%.*s>",
 | 
						|
            GRN_COLUMN_NAME_ID,
 | 
						|
            (int)GRN_TEXT_LEN(&inspected),
 | 
						|
            GRN_TEXT_VALUE(&inspected));
 | 
						|
        GRN_OBJ_FIN(ctx, &inspected);
 | 
						|
      } else {
 | 
						|
        id = GRN_UINT32_VALUE(&casted_value);
 | 
						|
      }
 | 
						|
      GRN_OBJ_FIN(ctx, &casted_value);
 | 
						|
      return id;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
bracket_close(grn_ctx *ctx, grn_loader *loader)
 | 
						|
{
 | 
						|
  grn_id id = GRN_ID_NIL;
 | 
						|
  grn_obj *value, *value_end, *id_value = NULL, *key_value = NULL;
 | 
						|
  grn_obj *col, **cols; /* Columns except _id and _key. */
 | 
						|
  uint32_t i, begin;
 | 
						|
  uint32_t ncols;   /* Number of columns except _id and _key. */
 | 
						|
  uint32_t nvalues; /* Number of values in brackets. */
 | 
						|
  uint32_t depth;
 | 
						|
  grn_bool is_record_load = GRN_FALSE;
 | 
						|
 | 
						|
  cols = (grn_obj **)GRN_BULK_HEAD(&loader->columns);
 | 
						|
  ncols = GRN_BULK_VSIZE(&loader->columns) / sizeof(grn_obj *);
 | 
						|
  GRN_UINT32_POP(&loader->level, begin);
 | 
						|
  value = (grn_obj *)GRN_TEXT_VALUE(&loader->values) + begin;
 | 
						|
  value_end = (grn_obj *)GRN_TEXT_VALUE(&loader->values) + loader->values_size;
 | 
						|
  GRN_ASSERT(value->header.domain == GRN_JSON_LOAD_OPEN_BRACKET);
 | 
						|
  GRN_UINT32_SET(ctx, value, loader->values_size - begin - 1);
 | 
						|
  value++;
 | 
						|
  depth = GRN_BULK_VSIZE(&loader->level);
 | 
						|
  if (depth > sizeof(uint32_t) * loader->emit_level) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (depth == 0 || !loader->table ||
 | 
						|
      loader->columns_status == GRN_LOADER_COLUMNS_BROKEN) {
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
  nvalues = values_len(ctx, value, value_end);
 | 
						|
 | 
						|
  if (loader->columns_status == GRN_LOADER_COLUMNS_UNSET) {
 | 
						|
    /*
 | 
						|
     * Target columns and _id or _key are not specified yet and values are
 | 
						|
     * handled as column names and "_id" or "_key".
 | 
						|
     */
 | 
						|
    for (i = 0; i < nvalues; i++) {
 | 
						|
      const char *col_name;
 | 
						|
      unsigned int col_name_size;
 | 
						|
      if (value->header.domain != GRN_DB_TEXT) {
 | 
						|
        grn_obj buffer;
 | 
						|
        GRN_TEXT_INIT(&buffer, 0);
 | 
						|
        grn_inspect(ctx, &buffer, value);
 | 
						|
        ERR(GRN_INVALID_ARGUMENT,
 | 
						|
            "column name must be string: <%.*s>",
 | 
						|
            (int)GRN_TEXT_LEN(&buffer), GRN_TEXT_VALUE(&buffer));
 | 
						|
        grn_loader_save_error(ctx, loader);
 | 
						|
        GRN_OBJ_FIN(ctx, &buffer);
 | 
						|
        loader->columns_status = GRN_LOADER_COLUMNS_BROKEN;
 | 
						|
        goto exit;
 | 
						|
      }
 | 
						|
      col_name = GRN_TEXT_VALUE(value);
 | 
						|
      col_name_size = GRN_TEXT_LEN(value);
 | 
						|
      col = grn_obj_column(ctx, loader->table, col_name, col_name_size);
 | 
						|
      if (!col) {
 | 
						|
        ERR(GRN_INVALID_ARGUMENT, "nonexistent column: <%.*s>",
 | 
						|
            col_name_size, col_name);
 | 
						|
        grn_loader_save_error(ctx, loader);
 | 
						|
        loader->columns_status = GRN_LOADER_COLUMNS_BROKEN;
 | 
						|
        goto exit;
 | 
						|
      }
 | 
						|
      if (name_equal(col_name, col_name_size, GRN_COLUMN_NAME_ID)) {
 | 
						|
        grn_obj_unlink(ctx, col);
 | 
						|
        if (loader->id_offset != -1 || loader->key_offset != -1) {
 | 
						|
          /* _id and _key must not appear more than once. */
 | 
						|
          if (loader->id_offset != -1) {
 | 
						|
            ERR(GRN_INVALID_ARGUMENT,
 | 
						|
                "duplicated id and key columns: <%s> at %d and <%s> at %d",
 | 
						|
                GRN_COLUMN_NAME_ID, i,
 | 
						|
                GRN_COLUMN_NAME_ID, loader->id_offset);
 | 
						|
          } else {
 | 
						|
            ERR(GRN_INVALID_ARGUMENT,
 | 
						|
                "duplicated id and key columns: <%s> at %d and <%s> at %d",
 | 
						|
                GRN_COLUMN_NAME_ID, i,
 | 
						|
                GRN_COLUMN_NAME_KEY, loader->key_offset);
 | 
						|
          }
 | 
						|
          grn_loader_save_error(ctx, loader);
 | 
						|
          loader->columns_status = GRN_LOADER_COLUMNS_BROKEN;
 | 
						|
          goto exit;
 | 
						|
        }
 | 
						|
        loader->id_offset = i;
 | 
						|
      } else if (name_equal(col_name, col_name_size, GRN_COLUMN_NAME_KEY)) {
 | 
						|
        grn_obj_unlink(ctx, col);
 | 
						|
        if (loader->id_offset != -1 || loader->key_offset != -1) {
 | 
						|
          /* _id and _key must not appear more than once. */
 | 
						|
          if (loader->id_offset != -1) {
 | 
						|
            ERR(GRN_INVALID_ARGUMENT,
 | 
						|
                "duplicated id and key columns: <%s> at %d and <%s> at %d",
 | 
						|
                GRN_COLUMN_NAME_KEY, i,
 | 
						|
                GRN_COLUMN_NAME_ID, loader->id_offset);
 | 
						|
          } else {
 | 
						|
            ERR(GRN_INVALID_ARGUMENT,
 | 
						|
                "duplicated id and key columns: <%s> at %d and <%s> at %d",
 | 
						|
                GRN_COLUMN_NAME_KEY, i,
 | 
						|
                GRN_COLUMN_NAME_KEY, loader->key_offset);
 | 
						|
          }
 | 
						|
          grn_loader_save_error(ctx, loader);
 | 
						|
          loader->columns_status = GRN_LOADER_COLUMNS_BROKEN;
 | 
						|
          goto exit;
 | 
						|
        }
 | 
						|
        loader->key_offset = i;
 | 
						|
      } else {
 | 
						|
        GRN_PTR_PUT(ctx, &loader->columns, col);
 | 
						|
      }
 | 
						|
      value++;
 | 
						|
    }
 | 
						|
    switch (loader->table->header.type) {
 | 
						|
    case GRN_TABLE_HASH_KEY :
 | 
						|
    case GRN_TABLE_PAT_KEY :
 | 
						|
    case GRN_TABLE_DAT_KEY :
 | 
						|
      if (loader->id_offset == -1 && loader->key_offset == -1) {
 | 
						|
        ERR(GRN_INVALID_ARGUMENT, "missing id or key column");
 | 
						|
        grn_loader_save_error(ctx, loader);
 | 
						|
        loader->columns_status = GRN_LOADER_COLUMNS_BROKEN;
 | 
						|
        goto exit;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    loader->columns_status = GRN_LOADER_COLUMNS_SET;
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
  is_record_load = GRN_TRUE;
 | 
						|
 | 
						|
  /* Target columns and _id or _key are already specified. */
 | 
						|
  if (!nvalues) {
 | 
						|
    /*
 | 
						|
     * Accept empty arrays because a dump command may output a load command
 | 
						|
     * which contains empty arrays for a table with deleted records.
 | 
						|
     */
 | 
						|
    id = grn_table_add(ctx, loader->table, NULL, 0, NULL);
 | 
						|
  } else {
 | 
						|
    uint32_t expected_nvalues = ncols;
 | 
						|
    if (loader->id_offset != -1 || loader->key_offset != -1) {
 | 
						|
      expected_nvalues++;
 | 
						|
    }
 | 
						|
    if (nvalues != expected_nvalues) {
 | 
						|
      ERR(GRN_INVALID_ARGUMENT,
 | 
						|
          "unexpected #values: expected:%u, actual:%u",
 | 
						|
          expected_nvalues, nvalues);
 | 
						|
      grn_loader_save_error(ctx, loader);
 | 
						|
      goto exit;
 | 
						|
    }
 | 
						|
    if (loader->id_offset != -1) {
 | 
						|
      id_value = value + loader->id_offset;
 | 
						|
      id = parse_id_value(ctx, id_value);
 | 
						|
      if (grn_table_at(ctx, loader->table, id) == GRN_ID_NIL) {
 | 
						|
        id = grn_table_add(ctx, loader->table, NULL, 0, NULL);
 | 
						|
      }
 | 
						|
    } else if (loader->key_offset != -1) {
 | 
						|
      key_value = value + loader->key_offset;
 | 
						|
      id = loader_add(ctx, key_value);
 | 
						|
    } else {
 | 
						|
      id = grn_table_add(ctx, loader->table, NULL, 0, NULL);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (id == GRN_ID_NIL) {
 | 
						|
    /* Target record is not available. */
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
  for (i = 0; i < nvalues; i++, value = values_next(ctx, value)) {
 | 
						|
    if ((uint) i == (uint) loader->id_offset ||
 | 
						|
        (uint) i == (uint) loader->key_offset) {
 | 
						|
       /* Skip _id and _key, because it's already used to get id. */
 | 
						|
       continue;
 | 
						|
    }
 | 
						|
    col = *cols;
 | 
						|
    if (value->header.domain == GRN_JSON_LOAD_OPEN_BRACKET) {
 | 
						|
      set_vector(ctx, col, id, value);
 | 
						|
    } else if (value->header.domain == GRN_JSON_LOAD_OPEN_BRACE) {
 | 
						|
      set_weight_vector(ctx, col, id, value);
 | 
						|
    } else {
 | 
						|
      grn_obj_set_value(ctx, col, id, value, GRN_OBJ_SET);
 | 
						|
    }
 | 
						|
    if (ctx->rc != GRN_SUCCESS) {
 | 
						|
      char column_name[GRN_TABLE_MAX_KEY_SIZE];
 | 
						|
      unsigned int column_name_size;
 | 
						|
      grn_loader_save_error(ctx, loader);
 | 
						|
      column_name_size = grn_obj_name(ctx, col, column_name,
 | 
						|
                                      GRN_TABLE_MAX_KEY_SIZE);
 | 
						|
      report_set_column_value_failure(ctx, key_value,
 | 
						|
                                      column_name, column_name_size,
 | 
						|
                                      value);
 | 
						|
      ERRCLR(ctx);
 | 
						|
    }
 | 
						|
    cols++;
 | 
						|
  }
 | 
						|
  if (loader->each) {
 | 
						|
    grn_obj *v = grn_expr_get_var_by_offset(ctx, loader->each, 0);
 | 
						|
    GRN_RECORD_SET(ctx, v, id);
 | 
						|
    grn_expr_exec(ctx, loader->each, 0);
 | 
						|
  }
 | 
						|
  loader->nrecords++;
 | 
						|
exit:
 | 
						|
  if (is_record_load) {
 | 
						|
    if (loader->output_ids) {
 | 
						|
      GRN_UINT32_PUT(ctx, &(loader->ids), id);
 | 
						|
    }
 | 
						|
    if (loader->output_errors) {
 | 
						|
      GRN_INT32_PUT(ctx, &(loader->return_codes), ctx->rc);
 | 
						|
      grn_vector_add_element(ctx,
 | 
						|
                             &(loader->error_messages),
 | 
						|
                             ctx->errbuf,
 | 
						|
                             strlen(ctx->errbuf),
 | 
						|
                             0,
 | 
						|
                             GRN_DB_TEXT);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  loader->values_size = begin;
 | 
						|
  ERRCLR(ctx);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
brace_close(grn_ctx *ctx, grn_loader *loader)
 | 
						|
{
 | 
						|
  grn_id id = GRN_ID_NIL;
 | 
						|
  grn_obj *value, *value_begin, *value_end;
 | 
						|
  grn_obj *id_value = NULL, *key_value = NULL;
 | 
						|
  uint32_t begin;
 | 
						|
 | 
						|
  GRN_UINT32_POP(&loader->level, begin);
 | 
						|
  value_begin = (grn_obj *)GRN_TEXT_VALUE(&loader->values) + begin;
 | 
						|
  value_end = (grn_obj *)GRN_TEXT_VALUE(&loader->values) + loader->values_size;
 | 
						|
  GRN_ASSERT(value->header.domain == GRN_JSON_LOAD_OPEN_BRACE);
 | 
						|
  GRN_UINT32_SET(ctx, value_begin, loader->values_size - begin - 1);
 | 
						|
  value_begin++;
 | 
						|
  if ((size_t) GRN_BULK_VSIZE(&loader->level) > sizeof(uint32_t) * loader->emit_level) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (!loader->table) {
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Scan values to find _id or _key. */
 | 
						|
  for (value = value_begin; value + 1 < value_end;
 | 
						|
       value = values_next(ctx, value)) {
 | 
						|
    const char *name = GRN_TEXT_VALUE(value);
 | 
						|
    unsigned int name_size = GRN_TEXT_LEN(value);
 | 
						|
    if (value->header.domain != GRN_DB_TEXT) {
 | 
						|
      grn_obj buffer;
 | 
						|
      GRN_TEXT_INIT(&buffer, 0);
 | 
						|
      grn_inspect(ctx, &buffer, value);
 | 
						|
      GRN_LOG(ctx, GRN_LOG_ERROR,
 | 
						|
              "column name must be string: <%.*s>",
 | 
						|
              (int)GRN_TEXT_LEN(&buffer), GRN_TEXT_VALUE(&buffer));
 | 
						|
      GRN_OBJ_FIN(ctx, &buffer);
 | 
						|
      goto exit;
 | 
						|
    }
 | 
						|
    value++;
 | 
						|
    if (name_equal(name, name_size, GRN_COLUMN_NAME_ID)) {
 | 
						|
      if (id_value || key_value) {
 | 
						|
        if (loader->table->header.type == GRN_TABLE_NO_KEY) {
 | 
						|
          GRN_LOG(ctx, GRN_LOG_ERROR, "duplicated '_id' column");
 | 
						|
          goto exit;
 | 
						|
        } else {
 | 
						|
          GRN_LOG(ctx, GRN_LOG_ERROR,
 | 
						|
                  "duplicated key columns: %s and %s",
 | 
						|
                  id_value ? GRN_COLUMN_NAME_ID : GRN_COLUMN_NAME_KEY,
 | 
						|
                  GRN_COLUMN_NAME_ID);
 | 
						|
          goto exit;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      id_value = value;
 | 
						|
    } else if (name_equal(name, name_size, GRN_COLUMN_NAME_KEY)) {
 | 
						|
      if (id_value || key_value) {
 | 
						|
        GRN_LOG(ctx, GRN_LOG_ERROR,
 | 
						|
                "duplicated key columns: %s and %s",
 | 
						|
                id_value ? GRN_COLUMN_NAME_ID : GRN_COLUMN_NAME_KEY,
 | 
						|
                GRN_COLUMN_NAME_KEY);
 | 
						|
        goto exit;
 | 
						|
      }
 | 
						|
      key_value = value;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  switch (loader->table->header.type) {
 | 
						|
  case GRN_TABLE_HASH_KEY :
 | 
						|
  case GRN_TABLE_PAT_KEY :
 | 
						|
  case GRN_TABLE_DAT_KEY :
 | 
						|
    /* The target table requires _id or _key. */
 | 
						|
    if (!id_value && !key_value) {
 | 
						|
      GRN_LOG(ctx, GRN_LOG_ERROR, "neither _key nor _id is assigned");
 | 
						|
      goto exit;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  default :
 | 
						|
    /* The target table does not have _key. */
 | 
						|
    if (key_value) {
 | 
						|
      GRN_LOG(ctx, GRN_LOG_ERROR, "nonexistent key value");
 | 
						|
      goto exit;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  if (id_value) {
 | 
						|
    id = parse_id_value(ctx, id_value);
 | 
						|
    if (grn_table_at(ctx, loader->table, id) == GRN_ID_NIL) {
 | 
						|
      if (ctx->rc == GRN_SUCCESS) {
 | 
						|
        id = grn_table_add(ctx, loader->table, NULL, 0, NULL);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } else if (key_value) {
 | 
						|
    id = loader_add(ctx, key_value);
 | 
						|
  } else {
 | 
						|
    id = grn_table_add(ctx, loader->table, NULL, 0, NULL);
 | 
						|
  }
 | 
						|
  if (id == GRN_ID_NIL) {
 | 
						|
    /* Target record is not available. */
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
  for (value = value_begin; value + 1 < value_end;
 | 
						|
       value = values_next(ctx, value)) {
 | 
						|
    grn_obj *col;
 | 
						|
    const char *name = GRN_TEXT_VALUE(value);
 | 
						|
    unsigned int name_size = GRN_TEXT_LEN(value);
 | 
						|
    value++;
 | 
						|
    if (value == id_value || value == key_value) {
 | 
						|
      /* Skip _id and _key, because it's already used to get id. */
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    col = grn_obj_column(ctx, loader->table, name, name_size);
 | 
						|
    if (!col) {
 | 
						|
      GRN_LOG(ctx, GRN_LOG_ERROR, "invalid column('%.*s')",
 | 
						|
              (int)name_size, name);
 | 
						|
      /* Automatic column creation is disabled. */
 | 
						|
      /*
 | 
						|
      if (value->header.domain == GRN_JSON_LOAD_OPEN_BRACKET) {
 | 
						|
        grn_obj *v = value + 1;
 | 
						|
        col = grn_column_create(ctx, loader->table, name, name_size,
 | 
						|
                                NULL, GRN_OBJ_PERSISTENT|GRN_OBJ_COLUMN_VECTOR,
 | 
						|
                                grn_ctx_at(ctx, v->header.domain));
 | 
						|
      } else {
 | 
						|
        col = grn_column_create(ctx, loader->table, name, name_size,
 | 
						|
                                NULL, GRN_OBJ_PERSISTENT,
 | 
						|
                                grn_ctx_at(ctx, value->header.domain));
 | 
						|
      }
 | 
						|
      */
 | 
						|
    } else {
 | 
						|
      if (value->header.domain == GRN_JSON_LOAD_OPEN_BRACKET) {
 | 
						|
        set_vector(ctx, col, id, value);
 | 
						|
      } else if (value->header.domain == GRN_JSON_LOAD_OPEN_BRACE) {
 | 
						|
        set_weight_vector(ctx, col, id, value);
 | 
						|
      } else {
 | 
						|
        grn_obj_set_value(ctx, col, id, value, GRN_OBJ_SET);
 | 
						|
      }
 | 
						|
      if (ctx->rc != GRN_SUCCESS) {
 | 
						|
        grn_loader_save_error(ctx, loader);
 | 
						|
        report_set_column_value_failure(ctx, key_value,
 | 
						|
                                        name, name_size, value);
 | 
						|
        ERRCLR(ctx);
 | 
						|
      }
 | 
						|
      grn_obj_unlink(ctx, col);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (loader->each) {
 | 
						|
    value = grn_expr_get_var_by_offset(ctx, loader->each, 0);
 | 
						|
    GRN_RECORD_SET(ctx, value, id);
 | 
						|
    grn_expr_exec(ctx, loader->each, 0);
 | 
						|
  }
 | 
						|
  loader->nrecords++;
 | 
						|
exit:
 | 
						|
  if (loader->output_ids) {
 | 
						|
    GRN_UINT32_PUT(ctx, &(loader->ids), id);
 | 
						|
  }
 | 
						|
  if (loader->output_errors) {
 | 
						|
    GRN_INT32_PUT(ctx, &(loader->return_codes), ctx->rc);
 | 
						|
    grn_vector_add_element(ctx,
 | 
						|
                           &(loader->error_messages),
 | 
						|
                           ctx->errbuf,
 | 
						|
                           strlen(ctx->errbuf),
 | 
						|
                           0,
 | 
						|
                           GRN_DB_TEXT);
 | 
						|
  }
 | 
						|
  loader->values_size = begin;
 | 
						|
  ERRCLR(ctx);
 | 
						|
}
 | 
						|
 | 
						|
#define JSON_READ_OPEN_BRACKET() do {\
 | 
						|
  GRN_UINT32_PUT(ctx, &loader->level, loader->values_size);\
 | 
						|
  values_add(ctx, loader);\
 | 
						|
  loader->last->header.domain = GRN_JSON_LOAD_OPEN_BRACKET;\
 | 
						|
  loader->stat = GRN_LOADER_TOKEN;\
 | 
						|
  str++;\
 | 
						|
} while (0)
 | 
						|
 | 
						|
#define JSON_READ_OPEN_BRACE() do {\
 | 
						|
  GRN_UINT32_PUT(ctx, &loader->level, loader->values_size);\
 | 
						|
  values_add(ctx, loader);\
 | 
						|
  loader->last->header.domain = GRN_JSON_LOAD_OPEN_BRACE;\
 | 
						|
  loader->stat = GRN_LOADER_TOKEN;\
 | 
						|
  str++;\
 | 
						|
} while (0)
 | 
						|
 | 
						|
static void
 | 
						|
json_read(grn_ctx *ctx, grn_loader *loader, const char *str, unsigned int str_len)
 | 
						|
{
 | 
						|
  const char *const beg = str;
 | 
						|
  char c;
 | 
						|
  int len;
 | 
						|
  const char *se = str + str_len;
 | 
						|
  while (str < se) {
 | 
						|
    c = *str;
 | 
						|
    switch (loader->stat) {
 | 
						|
    case GRN_LOADER_BEGIN :
 | 
						|
      if ((len = grn_isspace(str, ctx->encoding))) {
 | 
						|
        str += len;
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      switch (c) {
 | 
						|
      case '[' :
 | 
						|
        JSON_READ_OPEN_BRACKET();
 | 
						|
        break;
 | 
						|
      case '{' :
 | 
						|
        JSON_READ_OPEN_BRACE();
 | 
						|
        break;
 | 
						|
      default :
 | 
						|
        ERR(GRN_INVALID_ARGUMENT,
 | 
						|
            "JSON must start with '[' or '{': <%.*s>", str_len, beg);
 | 
						|
        loader->stat = GRN_LOADER_END;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case GRN_LOADER_TOKEN :
 | 
						|
      if ((len = grn_isspace(str, ctx->encoding))) {
 | 
						|
        str += len;
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      switch (c) {
 | 
						|
      case '"' :
 | 
						|
        loader->stat = GRN_LOADER_STRING;
 | 
						|
        values_add(ctx, loader);
 | 
						|
        str++;
 | 
						|
        break;
 | 
						|
      case '[' :
 | 
						|
        JSON_READ_OPEN_BRACKET();
 | 
						|
        break;
 | 
						|
      case '{' :
 | 
						|
        JSON_READ_OPEN_BRACE();
 | 
						|
        break;
 | 
						|
      case ':' :
 | 
						|
        str++;
 | 
						|
        break;
 | 
						|
      case ',' :
 | 
						|
        str++;
 | 
						|
        break;
 | 
						|
      case ']' :
 | 
						|
        bracket_close(ctx, loader);
 | 
						|
        loader->stat = GRN_BULK_VSIZE(&loader->level) ? GRN_LOADER_TOKEN : GRN_LOADER_END;
 | 
						|
        if (ctx->rc == GRN_CANCEL) {
 | 
						|
          loader->stat = GRN_LOADER_END;
 | 
						|
        }
 | 
						|
        str++;
 | 
						|
        break;
 | 
						|
      case '}' :
 | 
						|
        brace_close(ctx, loader);
 | 
						|
        loader->stat = GRN_BULK_VSIZE(&loader->level) ? GRN_LOADER_TOKEN : GRN_LOADER_END;
 | 
						|
        if (ctx->rc == GRN_CANCEL) {
 | 
						|
          loader->stat = GRN_LOADER_END;
 | 
						|
        }
 | 
						|
        str++;
 | 
						|
        break;
 | 
						|
      case '+' : case '-' : case '0' : case '1' : case '2' : case '3' :
 | 
						|
      case '4' : case '5' : case '6' : case '7' : case '8' : case '9' :
 | 
						|
        loader->stat = GRN_LOADER_NUMBER;
 | 
						|
        values_add(ctx, loader);
 | 
						|
        break;
 | 
						|
      default :
 | 
						|
        if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('_' == c)) {
 | 
						|
          loader->stat = GRN_LOADER_SYMBOL;
 | 
						|
          values_add(ctx, loader);
 | 
						|
        } else {
 | 
						|
          if ((len = grn_charlen(ctx, str, se))) {
 | 
						|
            GRN_LOG(ctx, GRN_LOG_ERROR, "ignored invalid char('%c') at", c);
 | 
						|
            GRN_LOG(ctx, GRN_LOG_ERROR, "%.*s", (int)(str - beg) + len, beg);
 | 
						|
            GRN_LOG(ctx, GRN_LOG_ERROR, "%*s", (int)(str - beg) + 1, "^");
 | 
						|
            str += len;
 | 
						|
          } else {
 | 
						|
            GRN_LOG(ctx, GRN_LOG_ERROR, "ignored invalid char(\\x%.2x) after", c);
 | 
						|
            GRN_LOG(ctx, GRN_LOG_ERROR, "%.*s", (int)(str - beg), beg);
 | 
						|
            str = se;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case GRN_LOADER_SYMBOL :
 | 
						|
      if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') ||
 | 
						|
          ('0' <= c && c <= '9') || ('_' == c)) {
 | 
						|
        GRN_TEXT_PUTC(ctx, loader->last, c);
 | 
						|
        str++;
 | 
						|
      } else {
 | 
						|
        char *v = GRN_TEXT_VALUE(loader->last);
 | 
						|
        switch (*v) {
 | 
						|
        case 'n' :
 | 
						|
          if (GRN_TEXT_LEN(loader->last) == 4 && !memcmp(v, "null", 4)) {
 | 
						|
            loader->last->header.domain = GRN_DB_VOID;
 | 
						|
            GRN_BULK_REWIND(loader->last);
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        case 't' :
 | 
						|
          if (GRN_TEXT_LEN(loader->last) == 4 && !memcmp(v, "true", 4)) {
 | 
						|
            loader->last->header.domain = GRN_DB_BOOL;
 | 
						|
            GRN_BOOL_SET(ctx, loader->last, GRN_TRUE);
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        case 'f' :
 | 
						|
          if (GRN_TEXT_LEN(loader->last) == 5 && !memcmp(v, "false", 5)) {
 | 
						|
            loader->last->header.domain = GRN_DB_BOOL;
 | 
						|
            GRN_BOOL_SET(ctx, loader->last, GRN_FALSE);
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        default :
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        loader->stat = GRN_BULK_VSIZE(&loader->level) ? GRN_LOADER_TOKEN : GRN_LOADER_END;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case GRN_LOADER_NUMBER :
 | 
						|
      switch (c) {
 | 
						|
      case '+' : case '-' : case '.' : case 'e' : case 'E' :
 | 
						|
      case '0' : case '1' : case '2' : case '3' : case '4' :
 | 
						|
      case '5' : case '6' : case '7' : case '8' : case '9' :
 | 
						|
        GRN_TEXT_PUTC(ctx, loader->last, c);
 | 
						|
        str++;
 | 
						|
        break;
 | 
						|
      default :
 | 
						|
        {
 | 
						|
          const char *cur, *str = GRN_BULK_HEAD(loader->last);
 | 
						|
          const char *str_end = GRN_BULK_CURR(loader->last);
 | 
						|
          int64_t i = grn_atoll(str, str_end, &cur);
 | 
						|
          if (cur == str_end) {
 | 
						|
            loader->last->header.domain = GRN_DB_INT64;
 | 
						|
            GRN_INT64_SET(ctx, loader->last, i);
 | 
						|
          } else if (cur != str) {
 | 
						|
            uint64_t i = grn_atoull(str, str_end, &cur);
 | 
						|
            if (cur == str_end) {
 | 
						|
              loader->last->header.domain = GRN_DB_UINT64;
 | 
						|
              GRN_UINT64_SET(ctx, loader->last, i);
 | 
						|
            } else if (cur != str) {
 | 
						|
              double d;
 | 
						|
              char *end;
 | 
						|
              grn_obj buf;
 | 
						|
              GRN_TEXT_INIT(&buf, 0);
 | 
						|
              GRN_TEXT_PUT(ctx, &buf, str, GRN_BULK_VSIZE(loader->last));
 | 
						|
              GRN_TEXT_PUTC(ctx, &buf, '\0');
 | 
						|
              errno = 0;
 | 
						|
              d = strtod(GRN_TEXT_VALUE(&buf), &end);
 | 
						|
              if (!errno && end + 1 == GRN_BULK_CURR(&buf)) {
 | 
						|
                loader->last->header.domain = GRN_DB_FLOAT;
 | 
						|
                GRN_FLOAT_SET(ctx, loader->last, d);
 | 
						|
              }
 | 
						|
              GRN_OBJ_FIN(ctx, &buf);
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
        loader->stat = GRN_BULK_VSIZE(&loader->level) ? GRN_LOADER_TOKEN : GRN_LOADER_END;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case GRN_LOADER_STRING :
 | 
						|
      switch (c) {
 | 
						|
      case '\\' :
 | 
						|
        loader->stat = GRN_LOADER_STRING_ESC;
 | 
						|
        str++;
 | 
						|
        break;
 | 
						|
      case '"' :
 | 
						|
        str++;
 | 
						|
        loader->stat = GRN_BULK_VSIZE(&loader->level) ? GRN_LOADER_TOKEN : GRN_LOADER_END;
 | 
						|
        /*
 | 
						|
        *(GRN_BULK_CURR(loader->last)) = '\0';
 | 
						|
        GRN_LOG(ctx, GRN_LOG_ALERT, "read str(%s)", GRN_TEXT_VALUE(loader->last));
 | 
						|
        */
 | 
						|
        break;
 | 
						|
      default :
 | 
						|
        if ((len = grn_charlen(ctx, str, se))) {
 | 
						|
          GRN_TEXT_PUT(ctx, loader->last, str, len);
 | 
						|
          str += len;
 | 
						|
        } else {
 | 
						|
          GRN_LOG(ctx, GRN_LOG_ERROR, "ignored invalid char(\\x%.2x) after", c);
 | 
						|
          GRN_LOG(ctx, GRN_LOG_ERROR, "%.*s", (int)(str - beg), beg);
 | 
						|
          str = se;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case GRN_LOADER_STRING_ESC :
 | 
						|
      switch (c) {
 | 
						|
      case 'b' :
 | 
						|
        GRN_TEXT_PUTC(ctx, loader->last, '\b');
 | 
						|
        loader->stat = GRN_LOADER_STRING;
 | 
						|
        break;
 | 
						|
      case 'f' :
 | 
						|
        GRN_TEXT_PUTC(ctx, loader->last, '\f');
 | 
						|
        loader->stat = GRN_LOADER_STRING;
 | 
						|
        break;
 | 
						|
      case 'n' :
 | 
						|
        GRN_TEXT_PUTC(ctx, loader->last, '\n');
 | 
						|
        loader->stat = GRN_LOADER_STRING;
 | 
						|
        break;
 | 
						|
      case 'r' :
 | 
						|
        GRN_TEXT_PUTC(ctx, loader->last, '\r');
 | 
						|
        loader->stat = GRN_LOADER_STRING;
 | 
						|
        break;
 | 
						|
      case 't' :
 | 
						|
        GRN_TEXT_PUTC(ctx, loader->last, '\t');
 | 
						|
        loader->stat = GRN_LOADER_STRING;
 | 
						|
        break;
 | 
						|
      case 'u' :
 | 
						|
        loader->stat = GRN_LOADER_UNICODE0;
 | 
						|
        break;
 | 
						|
      default :
 | 
						|
        GRN_TEXT_PUTC(ctx, loader->last, c);
 | 
						|
        loader->stat = GRN_LOADER_STRING;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      str++;
 | 
						|
      break;
 | 
						|
    case GRN_LOADER_UNICODE0 :
 | 
						|
      switch (c) {
 | 
						|
      case '0' : case '1' : case '2' : case '3' : case '4' :
 | 
						|
      case '5' : case '6' : case '7' : case '8' : case '9' :
 | 
						|
        loader->unichar = (c - '0') * 0x1000;
 | 
						|
        break;
 | 
						|
      case 'a' : case 'b' : case 'c' : case 'd' : case 'e' : case 'f' :
 | 
						|
        loader->unichar = (c - 'a' + 10) * 0x1000;
 | 
						|
        break;
 | 
						|
      case 'A' : case 'B' : case 'C' : case 'D' : case 'E' : case 'F' :
 | 
						|
        loader->unichar = (c - 'A' + 10) * 0x1000;
 | 
						|
        break;
 | 
						|
      default :
 | 
						|
        ;// todo : error
 | 
						|
      }
 | 
						|
      loader->stat = GRN_LOADER_UNICODE1;
 | 
						|
      str++;
 | 
						|
      break;
 | 
						|
    case GRN_LOADER_UNICODE1 :
 | 
						|
      switch (c) {
 | 
						|
      case '0' : case '1' : case '2' : case '3' : case '4' :
 | 
						|
      case '5' : case '6' : case '7' : case '8' : case '9' :
 | 
						|
        loader->unichar += (c - '0') * 0x100;
 | 
						|
        break;
 | 
						|
      case 'a' : case 'b' : case 'c' : case 'd' : case 'e' : case 'f' :
 | 
						|
        loader->unichar += (c - 'a' + 10) * 0x100;
 | 
						|
        break;
 | 
						|
      case 'A' : case 'B' : case 'C' : case 'D' : case 'E' : case 'F' :
 | 
						|
        loader->unichar += (c - 'A' + 10) * 0x100;
 | 
						|
        break;
 | 
						|
      default :
 | 
						|
        ;// todo : error
 | 
						|
      }
 | 
						|
      loader->stat = GRN_LOADER_UNICODE2;
 | 
						|
      str++;
 | 
						|
      break;
 | 
						|
    case GRN_LOADER_UNICODE2 :
 | 
						|
      switch (c) {
 | 
						|
      case '0' : case '1' : case '2' : case '3' : case '4' :
 | 
						|
      case '5' : case '6' : case '7' : case '8' : case '9' :
 | 
						|
        loader->unichar += (c - '0') * 0x10;
 | 
						|
        break;
 | 
						|
      case 'a' : case 'b' : case 'c' : case 'd' : case 'e' : case 'f' :
 | 
						|
        loader->unichar += (c - 'a' + 10) * 0x10;
 | 
						|
        break;
 | 
						|
      case 'A' : case 'B' : case 'C' : case 'D' : case 'E' : case 'F' :
 | 
						|
        loader->unichar += (c - 'A' + 10) * 0x10;
 | 
						|
        break;
 | 
						|
      default :
 | 
						|
        ;// todo : error
 | 
						|
      }
 | 
						|
      loader->stat = GRN_LOADER_UNICODE3;
 | 
						|
      str++;
 | 
						|
      break;
 | 
						|
    case GRN_LOADER_UNICODE3 :
 | 
						|
      switch (c) {
 | 
						|
      case '0' : case '1' : case '2' : case '3' : case '4' :
 | 
						|
      case '5' : case '6' : case '7' : case '8' : case '9' :
 | 
						|
        loader->unichar += (c - '0');
 | 
						|
        break;
 | 
						|
      case 'a' : case 'b' : case 'c' : case 'd' : case 'e' : case 'f' :
 | 
						|
        loader->unichar += (c - 'a' + 10);
 | 
						|
        break;
 | 
						|
      case 'A' : case 'B' : case 'C' : case 'D' : case 'E' : case 'F' :
 | 
						|
        loader->unichar += (c - 'A' + 10);
 | 
						|
        break;
 | 
						|
      default :
 | 
						|
        ;// todo : error
 | 
						|
      }
 | 
						|
      {
 | 
						|
        uint32_t u = loader->unichar;
 | 
						|
        if (u < 0x80) {
 | 
						|
          GRN_TEXT_PUTC(ctx, loader->last, u);
 | 
						|
        } else {
 | 
						|
          if (u < 0x800) {
 | 
						|
            GRN_TEXT_PUTC(ctx, loader->last, ((u >> 6) & 0x1f) | 0xc0);
 | 
						|
          } else {
 | 
						|
            GRN_TEXT_PUTC(ctx, loader->last, (u >> 12) | 0xe0);
 | 
						|
            GRN_TEXT_PUTC(ctx, loader->last, ((u >> 6) & 0x3f) | 0x80);
 | 
						|
          }
 | 
						|
          GRN_TEXT_PUTC(ctx, loader->last, (u & 0x3f) | 0x80);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      loader->stat = GRN_LOADER_STRING;
 | 
						|
      str++;
 | 
						|
      break;
 | 
						|
    case GRN_LOADER_END :
 | 
						|
      str = se;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
#undef JSON_READ_OPEN_BRACKET
 | 
						|
#undef JSON_READ_OPEN_BRACE
 | 
						|
 | 
						|
/*
 | 
						|
 * grn_loader_parse_columns parses a columns parameter.
 | 
						|
 * Columns except _id and _key are appended to loader->columns.
 | 
						|
 * If it contains _id or _key, loader->id_offset or loader->key_offset is set.
 | 
						|
 */
 | 
						|
static grn_rc
 | 
						|
grn_loader_parse_columns(grn_ctx *ctx, grn_loader *loader,
 | 
						|
                         const char *str, unsigned int str_size)
 | 
						|
{
 | 
						|
  const char *ptr = str, *ptr_end = ptr + str_size, *rest;
 | 
						|
  const char *tokens[256], *token_end;
 | 
						|
  while (ptr < ptr_end) {
 | 
						|
    int i, n = grn_tokenize(ptr, ptr_end - ptr, tokens, 256, &rest);
 | 
						|
    for (i = 0; i < n; i++) {
 | 
						|
      grn_obj *column;
 | 
						|
      token_end = tokens[i];
 | 
						|
      while (ptr < token_end && (' ' == *ptr || ',' == *ptr)) {
 | 
						|
        ptr++;
 | 
						|
      }
 | 
						|
      column = grn_obj_column(ctx, loader->table, ptr, token_end - ptr);
 | 
						|
      if (!column) {
 | 
						|
        ERR(GRN_INVALID_ARGUMENT, "nonexistent column: <%.*s>",
 | 
						|
            (int)(token_end - ptr), ptr);
 | 
						|
        return ctx->rc;
 | 
						|
      }
 | 
						|
      if (name_equal(ptr, token_end - ptr, GRN_COLUMN_NAME_ID)) {
 | 
						|
        grn_obj_unlink(ctx, column);
 | 
						|
        if (loader->id_offset != -1 || loader->key_offset != -1) {
 | 
						|
          /* _id and _key must not appear more than once. */
 | 
						|
          if (loader->id_offset != -1) {
 | 
						|
            ERR(GRN_INVALID_ARGUMENT,
 | 
						|
                "duplicated id and key columns: <%s> at %d and <%s> at %d",
 | 
						|
                GRN_COLUMN_NAME_ID, i,
 | 
						|
                GRN_COLUMN_NAME_ID, loader->id_offset);
 | 
						|
          } else {
 | 
						|
            ERR(GRN_INVALID_ARGUMENT,
 | 
						|
                "duplicated id and key columns: <%s> at %d and <%s> at %d",
 | 
						|
                GRN_COLUMN_NAME_ID, i,
 | 
						|
                GRN_COLUMN_NAME_KEY, loader->key_offset);
 | 
						|
          }
 | 
						|
          return ctx->rc;
 | 
						|
        }
 | 
						|
        loader->id_offset = i;
 | 
						|
      } else if (name_equal(ptr, token_end - ptr, GRN_COLUMN_NAME_KEY)) {
 | 
						|
        grn_obj_unlink(ctx, column);
 | 
						|
        if (loader->id_offset != -1 || loader->key_offset != -1) {
 | 
						|
          /* _id and _key must not appear more than once. */
 | 
						|
          if (loader->id_offset != -1) {
 | 
						|
            ERR(GRN_INVALID_ARGUMENT,
 | 
						|
                "duplicated id and key columns: <%s> at %d and <%s> at %d",
 | 
						|
                GRN_COLUMN_NAME_KEY, i,
 | 
						|
                GRN_COLUMN_NAME_ID, loader->id_offset);
 | 
						|
          } else {
 | 
						|
            ERR(GRN_INVALID_ARGUMENT,
 | 
						|
                "duplicated id and key columns: <%s> at %d and <%s> at %d",
 | 
						|
                GRN_COLUMN_NAME_KEY, i,
 | 
						|
                GRN_COLUMN_NAME_KEY, loader->key_offset);
 | 
						|
          }
 | 
						|
          return ctx->rc;
 | 
						|
        }
 | 
						|
        loader->key_offset = i;
 | 
						|
      } else {
 | 
						|
        GRN_PTR_PUT(ctx, &loader->columns, column);
 | 
						|
      }
 | 
						|
      ptr = token_end;
 | 
						|
    }
 | 
						|
    ptr = rest;
 | 
						|
  }
 | 
						|
  switch (loader->table->header.type) {
 | 
						|
  case GRN_TABLE_HASH_KEY :
 | 
						|
  case GRN_TABLE_PAT_KEY :
 | 
						|
  case GRN_TABLE_DAT_KEY :
 | 
						|
    if (loader->id_offset == -1 && loader->key_offset == -1) {
 | 
						|
      ERR(GRN_INVALID_ARGUMENT, "missing id or key column");
 | 
						|
      return ctx->rc;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  return ctx->rc;
 | 
						|
}
 | 
						|
 | 
						|
static grn_com_addr *addr;
 | 
						|
 | 
						|
void
 | 
						|
grn_load_internal(grn_ctx *ctx, grn_load_input *input)
 | 
						|
{
 | 
						|
  grn_loader *loader = &ctx->impl->loader;
 | 
						|
 | 
						|
  loader->emit_level = input->emit_level;
 | 
						|
  if (ctx->impl->edge) {
 | 
						|
    grn_edge *edge = grn_edges_add_communicator(ctx, addr);
 | 
						|
    grn_obj *msg = grn_msg_open(ctx, edge->com, &ctx->impl->edge->send_old);
 | 
						|
    /* build msg */
 | 
						|
    grn_edge_dispatch(ctx, edge, msg);
 | 
						|
  }
 | 
						|
  if (input->table.length > 0) {
 | 
						|
    grn_ctx_loader_clear(ctx);
 | 
						|
    loader->input_type = input->type;
 | 
						|
    if (grn_db_check_name(ctx, input->table.value, input->table.length)) {
 | 
						|
      GRN_DB_CHECK_NAME_ERR("[table][load]",
 | 
						|
                            input->table.value,
 | 
						|
                            (int)(input->table.length));
 | 
						|
      loader->stat = GRN_LOADER_END;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    loader->table = grn_ctx_get(ctx, input->table.value, input->table.length);
 | 
						|
    if (!loader->table) {
 | 
						|
      ERR(GRN_INVALID_ARGUMENT,
 | 
						|
          "nonexistent table: <%.*s>",
 | 
						|
          (int)(input->table.length),
 | 
						|
          input->table.value);
 | 
						|
      loader->stat = GRN_LOADER_END;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (input->columns.length > 0) {
 | 
						|
      grn_rc rc = grn_loader_parse_columns(ctx,
 | 
						|
                                           loader,
 | 
						|
                                           input->columns.value,
 | 
						|
                                           input->columns.length);
 | 
						|
      if (rc != GRN_SUCCESS) {
 | 
						|
        loader->columns_status = GRN_LOADER_COLUMNS_BROKEN;
 | 
						|
        loader->stat = GRN_LOADER_END;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      loader->columns_status = GRN_LOADER_COLUMNS_SET;
 | 
						|
    }
 | 
						|
    if (input->if_exists.length > 0) {
 | 
						|
      grn_obj *v;
 | 
						|
      GRN_EXPR_CREATE_FOR_QUERY(ctx, loader->table, loader->ifexists, v);
 | 
						|
      if (loader->ifexists && v) {
 | 
						|
        grn_expr_parse(ctx,
 | 
						|
                       loader->ifexists,
 | 
						|
                       input->if_exists.value,
 | 
						|
                       input->if_exists.length,
 | 
						|
                       NULL, GRN_OP_EQUAL, GRN_OP_AND,
 | 
						|
                       GRN_EXPR_SYNTAX_SCRIPT|GRN_EXPR_ALLOW_UPDATE);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (input->each.length > 0) {
 | 
						|
      grn_obj *v;
 | 
						|
      GRN_EXPR_CREATE_FOR_QUERY(ctx, loader->table, loader->each, v);
 | 
						|
      if (loader->each && v) {
 | 
						|
        grn_expr_parse(ctx, loader->each,
 | 
						|
                       input->each.value,
 | 
						|
                       input->each.length,
 | 
						|
                       NULL, GRN_OP_EQUAL, GRN_OP_AND,
 | 
						|
                       GRN_EXPR_SYNTAX_SCRIPT|GRN_EXPR_ALLOW_UPDATE);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    loader->output_ids = input->output_ids;
 | 
						|
    loader->output_errors = input->output_errors;
 | 
						|
  } else {
 | 
						|
    if (!loader->table) {
 | 
						|
      ERR(GRN_INVALID_ARGUMENT, "mandatory \"table\" parameter is absent");
 | 
						|
      loader->stat = GRN_LOADER_END;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  switch (loader->input_type) {
 | 
						|
  case GRN_CONTENT_JSON :
 | 
						|
    json_read(ctx, loader, input->values.value, input->values.length);
 | 
						|
    break;
 | 
						|
  case GRN_CONTENT_NONE :
 | 
						|
  case GRN_CONTENT_TSV :
 | 
						|
  case GRN_CONTENT_XML :
 | 
						|
  case GRN_CONTENT_MSGPACK :
 | 
						|
  case GRN_CONTENT_GROONGA_COMMAND_LIST :
 | 
						|
    ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "unsupported input_type");
 | 
						|
    loader->stat = GRN_LOADER_END;
 | 
						|
    // todo
 | 
						|
    break;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_load(grn_ctx *ctx, grn_content_type input_type,
 | 
						|
         const char *table, unsigned int table_len,
 | 
						|
         const char *columns, unsigned int columns_len,
 | 
						|
         const char *values, unsigned int values_len,
 | 
						|
         const char *ifexists, unsigned int ifexists_len,
 | 
						|
         const char *each, unsigned int each_len)
 | 
						|
{
 | 
						|
  if (!ctx || !ctx->impl) {
 | 
						|
    ERR(GRN_INVALID_ARGUMENT, "db not initialized");
 | 
						|
    return ctx->rc;
 | 
						|
  }
 | 
						|
  GRN_API_ENTER;
 | 
						|
  {
 | 
						|
    grn_load_input input;
 | 
						|
    input.type = input_type;
 | 
						|
    input.table.value = table;
 | 
						|
    input.table.length = table_len;
 | 
						|
    input.columns.value = columns;
 | 
						|
    input.columns.length = columns_len;
 | 
						|
    input.values.value = values;
 | 
						|
    input.values.length = values_len;
 | 
						|
    input.if_exists.value = ifexists;
 | 
						|
    input.if_exists.length = ifexists_len;
 | 
						|
    input.each.value = each;
 | 
						|
    input.each.length = each_len;
 | 
						|
    input.output_ids = GRN_FALSE;
 | 
						|
    input.output_errors = GRN_FALSE;
 | 
						|
    input.emit_level = 1;
 | 
						|
    grn_load_internal(ctx, &input);
 | 
						|
  }
 | 
						|
  GRN_API_RETURN(ctx->rc);
 | 
						|
}
 | 
						|
 | 
						|
PRAGMA_REENABLE_CHECK_STACK_FRAME
 |