mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 12:56:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			4434 lines
		
	
	
	
		
			126 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			4434 lines
		
	
	
	
		
			126 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Copyright (c) 2011, 2017, MariaDB Corporation.
 | 
						|
   Copyright (c) 2011, 2012, Oleksandr Byelkin
 | 
						|
 | 
						|
   Redistribution and use in source and binary forms, with or without
 | 
						|
   modification, are permitted provided that the following conditions are
 | 
						|
   met:
 | 
						|
 | 
						|
   1. Redistributions of source code must retain the above copyright
 | 
						|
   notice, this list of conditions and the following disclaimer.
 | 
						|
 | 
						|
   2. Redistributions in binary form must the following disclaimer in
 | 
						|
     the documentation and/or other materials provided with the
 | 
						|
     distribution.
 | 
						|
 | 
						|
   THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY
 | 
						|
   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
						|
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
						|
   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
 | 
						|
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
						|
   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
						|
   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 | 
						|
   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | 
						|
   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 | 
						|
   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 | 
						|
   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
						|
   SUCH DAMAGE.
 | 
						|
*/
 | 
						|
 | 
						|
/*
 | 
						|
 Numeric format:
 | 
						|
 ===============
 | 
						|
  * Fixed header part
 | 
						|
    1 byte flags:
 | 
						|
      0,1 bits - <offset size> - 1
 | 
						|
      2-7 bits - 0
 | 
						|
    2 bytes column counter
 | 
						|
  * Columns directory sorted by column number, each entry contains of:
 | 
						|
    2 bytes column number
 | 
						|
    <offset size> bytes (1-4) combined offset from beginning of
 | 
						|
      the data segment + 3 bit type
 | 
						|
  * Data of above columns size of data and length depend on type
 | 
						|
 | 
						|
 Columns with names:
 | 
						|
 ===================
 | 
						|
  * Fixed header part
 | 
						|
    1 byte flags:
 | 
						|
      0,1 bits - <offset size> - 2
 | 
						|
      2 bit    - 1 (means format with names)
 | 
						|
      3,4 bits - 00 (means <names offset size> - 2,
 | 
						|
                     now 2 is the only supported size)
 | 
						|
      5-7 bits - 0
 | 
						|
    2 bytes column counter
 | 
						|
  * Variable header part (now it is actually fixed part)
 | 
						|
    <names offset size> (2) bytes size of stored names pool
 | 
						|
  * Column directory sorted by names, each consists of
 | 
						|
    <names offset size> (2) bytes offset of name
 | 
						|
    <offset size> bytes (2-5)bytes combined offset from beginning of
 | 
						|
      the data segment + 4 bit type
 | 
						|
  * Names stored one after another
 | 
						|
  * Data of above columns size of data and length depend on type
 | 
						|
*/
 | 
						|
 | 
						|
#include "mysys_priv.h"
 | 
						|
#include <m_string.h>
 | 
						|
#include <ma_dyncol.h>
 | 
						|
#include <my_time.h>
 | 
						|
 | 
						|
uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
 | 
						|
			const char *from, uint32 from_length,
 | 
						|
			CHARSET_INFO *from_cs, uint *errors);
 | 
						|
/*
 | 
						|
  Flag byte bits
 | 
						|
 | 
						|
  2 bits which determinate size of offset in the header -1
 | 
						|
*/
 | 
						|
/* mask to get above bits */
 | 
						|
#define DYNCOL_FLG_OFFSET   (1U|2U)
 | 
						|
#define DYNCOL_FLG_NAMES    4U
 | 
						|
#define DYNCOL_FLG_NMOFFSET (8U|16U)
 | 
						|
/**
 | 
						|
  All known flags mask that could be set.
 | 
						|
 | 
						|
  @note DYNCOL_FLG_NMOFFSET should be 0 for now.
 | 
						|
*/
 | 
						|
#define DYNCOL_FLG_KNOWN  (1U|2U|4U)
 | 
						|
 | 
						|
/* formats */
 | 
						|
enum enum_dyncol_format
 | 
						|
{
 | 
						|
  dyncol_fmt_num= 0,
 | 
						|
  dyncol_fmt_str= 1
 | 
						|
};
 | 
						|
 | 
						|
/* dynamic column size reserve */
 | 
						|
#define DYNCOL_SYZERESERVE 80
 | 
						|
 | 
						|
#define DYNCOL_OFFSET_ERROR 0xffffffff
 | 
						|
 | 
						|
/* length of fixed string header 1 byte - flags, 2 bytes - columns counter */
 | 
						|
#define FIXED_HEADER_SIZE 3
 | 
						|
/*
 | 
						|
  length of fixed string header with names
 | 
						|
  1 byte - flags, 2 bytes - columns counter,  2 bytes - name pool size
 | 
						|
*/
 | 
						|
#define FIXED_HEADER_SIZE_NM 5
 | 
						|
 | 
						|
#define COLUMN_NUMBER_SIZE 2
 | 
						|
/* 2 bytes offset from the name pool */
 | 
						|
#define COLUMN_NAMEPTR_SIZE 2
 | 
						|
 | 
						|
#define MAX_OFFSET_LENGTH    4
 | 
						|
#define MAX_OFFSET_LENGTH_NM 5
 | 
						|
 | 
						|
#define DYNCOL_NUM_CHAR 6
 | 
						|
 | 
						|
my_bool mariadb_dyncol_has_names(DYNAMIC_COLUMN *str)
 | 
						|
{
 | 
						|
  if (str->length < 1)
 | 
						|
    return FALSE;
 | 
						|
  return MY_TEST(str->str[0] & DYNCOL_FLG_NAMES);
 | 
						|
}
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_time_store(DYNAMIC_COLUMN *str,
 | 
						|
                          MYSQL_TIME *value, enum enum_dyncol_format format);
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_date_store(DYNAMIC_COLUMN *str,
 | 
						|
                          MYSQL_TIME *value);
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_time_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                                  uchar *data, size_t length);
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_date_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                                  uchar *data, size_t length);
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_get_internal(DYNAMIC_COLUMN *str,
 | 
						|
                                DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                                uint num_key, LEX_STRING *str_key);
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_exists_internal(DYNAMIC_COLUMN *str, uint num_key,
 | 
						|
                               LEX_STRING *str_key);
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_update_many_fmt(DYNAMIC_COLUMN *str,
 | 
						|
                               uint add_column_count,
 | 
						|
                               void *column_keys,
 | 
						|
                               DYNAMIC_COLUMN_VALUE *values,
 | 
						|
                               my_bool string_keys);
 | 
						|
static int plan_sort_num(const void *a, const void *b);
 | 
						|
static int plan_sort_named(const void *a, const void *b);
 | 
						|
 | 
						|
/*
 | 
						|
  Structure to hold information about dynamic columns record and
 | 
						|
  iterate through it.
 | 
						|
*/
 | 
						|
 | 
						|
struct st_dyn_header
 | 
						|
{
 | 
						|
  uchar *header, *nmpool, *dtpool, *data_end;
 | 
						|
  size_t offset_size;
 | 
						|
  size_t entry_size;
 | 
						|
  size_t header_size;
 | 
						|
  size_t nmpool_size;
 | 
						|
  size_t data_size;
 | 
						|
  /* dyncol_fmt_num - numeric columns, dyncol_fmt_str - column names */
 | 
						|
  enum enum_dyncol_format format;
 | 
						|
  uint column_count;
 | 
						|
 | 
						|
  uchar *entry, *data, *name;
 | 
						|
  size_t offset;
 | 
						|
  size_t length;
 | 
						|
  enum enum_dynamic_column_type type;
 | 
						|
};
 | 
						|
 | 
						|
typedef struct st_dyn_header DYN_HEADER;
 | 
						|
 | 
						|
static inline my_bool read_fixed_header(DYN_HEADER *hdr,
 | 
						|
                                        DYNAMIC_COLUMN *str);
 | 
						|
static void set_fixed_header(DYNAMIC_COLUMN *str,
 | 
						|
                             uint offset_size,
 | 
						|
                             uint column_count);
 | 
						|
 | 
						|
/*
 | 
						|
  Calculate entry size (E) and header size (H) by offset size (O) and column
 | 
						|
  count (C) and fixed part of entry size (F).
 | 
						|
*/
 | 
						|
 | 
						|
#define calc_param(E,H,F,O,C) do { \
 | 
						|
  (*(E))= (O) + F;                 \
 | 
						|
  (*(H))= (*(E)) * (C);            \
 | 
						|
}while(0);
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Name pool size functions, for numeric format it is 0
 | 
						|
*/
 | 
						|
 | 
						|
static size_t name_size_num(void *keys __attribute__((unused)),
 | 
						|
                            uint i __attribute__((unused)))
 | 
						|
{
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Name pool size functions.
 | 
						|
*/
 | 
						|
static size_t name_size_named(void *keys, uint i)
 | 
						|
{
 | 
						|
  return ((LEX_STRING *) keys)[i].length;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Comparator function for references on column numbers for qsort
 | 
						|
  (numeric format)
 | 
						|
*/
 | 
						|
 | 
						|
static int column_sort_num(const void *a, const void *b)
 | 
						|
{
 | 
						|
  return **((uint **)a) - **((uint **)b);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Comparator function for references on column numbers for qsort
 | 
						|
  (names format)
 | 
						|
*/
 | 
						|
 | 
						|
int mariadb_dyncol_column_cmp_named(const LEX_STRING *s1, const LEX_STRING *s2)
 | 
						|
{
 | 
						|
  /*
 | 
						|
    We compare instead of subtraction to avoid data loss in case of huge
 | 
						|
    length difference (more then fit in int).
 | 
						|
  */
 | 
						|
  int rc= (s1->length > s2->length ? 1 :
 | 
						|
           (s1->length < s2->length ? -1 : 0));
 | 
						|
  if (rc == 0)
 | 
						|
    rc= memcmp((void *)s1->str, (void *)s2->str,
 | 
						|
               (size_t) s1->length);
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Comparator function for references on column numbers for qsort
 | 
						|
  (names format)
 | 
						|
*/
 | 
						|
 | 
						|
static int column_sort_named(const void *a, const void *b)
 | 
						|
{
 | 
						|
  return mariadb_dyncol_column_cmp_named(*((LEX_STRING **)a),
 | 
						|
                                         *((LEX_STRING **)b));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Check limit function (numeric format)
 | 
						|
*/
 | 
						|
 | 
						|
static my_bool check_limit_num(const void *val)
 | 
						|
{
 | 
						|
  return **((uint **)val) > UINT_MAX16;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Check limit function (names format)
 | 
						|
*/
 | 
						|
 | 
						|
static my_bool check_limit_named(const void *val)
 | 
						|
{
 | 
						|
  return (*((LEX_STRING **)val))->length > MAX_NAME_LENGTH;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Write numeric format static header part.
 | 
						|
*/
 | 
						|
 | 
						|
static void set_fixed_header_num(DYNAMIC_COLUMN *str, DYN_HEADER *hdr)
 | 
						|
{
 | 
						|
  set_fixed_header(str, (uint)hdr->offset_size, hdr->column_count);
 | 
						|
  hdr->header= (uchar *)str->str + FIXED_HEADER_SIZE;
 | 
						|
  hdr->nmpool= hdr->dtpool= hdr->header + hdr->header_size;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Write names format static header part.
 | 
						|
*/
 | 
						|
 | 
						|
static void set_fixed_header_named(DYNAMIC_COLUMN *str, DYN_HEADER *hdr)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(hdr->column_count <= 0xffff);
 | 
						|
  DBUG_ASSERT(hdr->offset_size <= MAX_OFFSET_LENGTH_NM);
 | 
						|
  /* size of data offset, named format flag, size of names offset (0 means 2) */
 | 
						|
  str->str[0]=
 | 
						|
    (char) (((uchar)str->str[0] & ~(DYNCOL_FLG_OFFSET | DYNCOL_FLG_NMOFFSET)) |
 | 
						|
            (hdr->offset_size - 2) | DYNCOL_FLG_NAMES);
 | 
						|
  int2store(str->str + 1, hdr->column_count);        /* columns number */
 | 
						|
  int2store(str->str + 3, hdr->nmpool_size);
 | 
						|
  hdr->header= (uchar *)str->str + FIXED_HEADER_SIZE_NM;
 | 
						|
  hdr->nmpool= hdr->header + hdr->header_size;
 | 
						|
  hdr->dtpool= hdr->nmpool + hdr->nmpool_size;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Store offset and type information in the given place
 | 
						|
 | 
						|
  @param place           Beginning of the index entry
 | 
						|
  @param offset_size     Size of offset field in bytes
 | 
						|
  @param type            Type to be written
 | 
						|
  @param offset          Offset to be written
 | 
						|
*/
 | 
						|
 | 
						|
static my_bool type_and_offset_store_num(uchar *place, size_t offset_size,
 | 
						|
                                         DYNAMIC_COLUMN_TYPE type,
 | 
						|
                                         size_t offset)
 | 
						|
{
 | 
						|
  ulong val = (((ulong) offset) << 3) | (type - 1);
 | 
						|
  DBUG_ASSERT(type != DYN_COL_NULL);
 | 
						|
  DBUG_ASSERT(((type - 1) & (~7U)) == 0); /* fit in 3 bits */
 | 
						|
  DBUG_ASSERT(offset_size >= 1 && offset_size <= 4);
 | 
						|
 | 
						|
  /* Index entry starts with column number; jump over it */
 | 
						|
  place+= COLUMN_NUMBER_SIZE;
 | 
						|
 | 
						|
  switch (offset_size) {
 | 
						|
  case 1:
 | 
						|
    if (offset >= 0x1f)          /* all 1 value is reserved */
 | 
						|
      return TRUE;
 | 
						|
    place[0]= (uchar)val;
 | 
						|
    break;
 | 
						|
  case 2:
 | 
						|
    if (offset >= 0x1fff)        /* all 1 value is reserved */
 | 
						|
      return TRUE;
 | 
						|
    int2store(place, val);
 | 
						|
    break;
 | 
						|
  case 3:
 | 
						|
    if (offset >= 0x1fffff)      /* all 1 value is reserved */
 | 
						|
      return TRUE;
 | 
						|
    int3store(place, val);
 | 
						|
    break;
 | 
						|
  case 4:
 | 
						|
    if (offset >= 0x1fffffff)    /* all 1 value is reserved */
 | 
						|
      return TRUE;
 | 
						|
    int4store(place, val);
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
      return TRUE;
 | 
						|
  }
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static my_bool type_and_offset_store_named(uchar *place, size_t offset_size,
 | 
						|
                                           DYNAMIC_COLUMN_TYPE type,
 | 
						|
                                           size_t offset)
 | 
						|
{
 | 
						|
  ulonglong val = (((ulong) offset) << 4) | (type - 1);
 | 
						|
  DBUG_ASSERT(type != DYN_COL_NULL);
 | 
						|
  DBUG_ASSERT(((type - 1) & (~0xfU)) == 0); /* fit in 4 bits */
 | 
						|
  DBUG_ASSERT(offset_size >= 2 && offset_size <= 5);
 | 
						|
 | 
						|
  /* Index entry starts with name offset; jump over it */
 | 
						|
  place+= COLUMN_NAMEPTR_SIZE;
 | 
						|
  switch (offset_size) {
 | 
						|
  case 2:
 | 
						|
    if (offset >= 0xfff)          /* all 1 value is reserved */
 | 
						|
      return TRUE;
 | 
						|
    int2store(place, val);
 | 
						|
    break;
 | 
						|
  case 3:
 | 
						|
    if (offset >= 0xfffff)        /* all 1 value is reserved */
 | 
						|
      return TRUE;
 | 
						|
    int3store(place, val);
 | 
						|
    break;
 | 
						|
  case 4:
 | 
						|
    if (offset >= 0xfffffff)      /* all 1 value is reserved */
 | 
						|
      return TRUE;
 | 
						|
    int4store(place, val);
 | 
						|
    break;
 | 
						|
  case 5:
 | 
						|
#if SIZEOF_SIZE_T > 4
 | 
						|
    if (offset >= 0xfffffffffull)    /* all 1 value is reserved */
 | 
						|
      return TRUE;
 | 
						|
#endif
 | 
						|
    int5store(place, val);
 | 
						|
    break;
 | 
						|
  case 1:
 | 
						|
  default:
 | 
						|
      return TRUE;
 | 
						|
  }
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Write numeric format header entry
 | 
						|
   2 bytes - column number
 | 
						|
   1-4 bytes - data offset combined with type
 | 
						|
 | 
						|
  @param hdr             descriptor of dynamic column record
 | 
						|
  @param column_key      pointer to uint (column number)
 | 
						|
  @param value           value which will be written (only type used)
 | 
						|
  @param offset          offset of the data
 | 
						|
*/
 | 
						|
 | 
						|
static my_bool put_header_entry_num(DYN_HEADER *hdr,
 | 
						|
                                    void *column_key,
 | 
						|
                                    DYNAMIC_COLUMN_VALUE *value,
 | 
						|
                                    size_t offset)
 | 
						|
{
 | 
						|
  uint *column_number= (uint *)column_key;
 | 
						|
  int2store(hdr->entry, *column_number);
 | 
						|
  DBUG_ASSERT(hdr->nmpool_size == 0);
 | 
						|
  if (type_and_offset_store_num(hdr->entry, hdr->offset_size,
 | 
						|
                                value->type,
 | 
						|
                                offset))
 | 
						|
      return TRUE;
 | 
						|
  hdr->entry= hdr->entry + hdr->entry_size;
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Write names format header entry
 | 
						|
   1 byte - name length
 | 
						|
   2 bytes - name offset in the name pool
 | 
						|
   1-4 bytes - data offset combined with type
 | 
						|
 | 
						|
  @param hdr             descriptor of dynamic column record
 | 
						|
  @param column_key      pointer to LEX_STRING (column name)
 | 
						|
  @param value           value which will be written (only type used)
 | 
						|
  @param offset          offset of the data
 | 
						|
*/
 | 
						|
 | 
						|
static my_bool put_header_entry_named(DYN_HEADER *hdr,
 | 
						|
                                      void *column_key,
 | 
						|
                                      DYNAMIC_COLUMN_VALUE *value,
 | 
						|
                                      size_t offset)
 | 
						|
{
 | 
						|
  LEX_STRING *column_name= (LEX_STRING *)column_key;
 | 
						|
  DBUG_ASSERT(column_name->length <= MAX_NAME_LENGTH);
 | 
						|
  DBUG_ASSERT(hdr->name - hdr->nmpool < (long) 0x10000L);
 | 
						|
  int2store(hdr->entry, hdr->name - hdr->nmpool);
 | 
						|
  memcpy(hdr->name, column_name->str, column_name->length);
 | 
						|
  DBUG_ASSERT(hdr->nmpool_size != 0 || column_name->length == 0);
 | 
						|
  if (type_and_offset_store_named(hdr->entry, hdr->offset_size,
 | 
						|
                                  value->type,
 | 
						|
                                  offset))
 | 
						|
    return TRUE;
 | 
						|
  hdr->entry+= hdr->entry_size;
 | 
						|
  hdr->name+= column_name->length;
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate length of offset field for given data length
 | 
						|
 | 
						|
  @param data_length     Length of the data segment
 | 
						|
 | 
						|
  @return number of bytes
 | 
						|
*/
 | 
						|
 | 
						|
static size_t dynamic_column_offset_bytes_num(size_t data_length)
 | 
						|
{
 | 
						|
  if (data_length < 0x1f)                /* all 1 value is reserved */
 | 
						|
    return 1;
 | 
						|
  if (data_length < 0x1fff)              /* all 1 value is reserved */
 | 
						|
    return 2;
 | 
						|
  if (data_length < 0x1fffff)            /* all 1 value is reserved */
 | 
						|
    return 3;
 | 
						|
  if (data_length < 0x1fffffff)          /* all 1 value is reserved */
 | 
						|
    return 4;
 | 
						|
  return MAX_OFFSET_LENGTH + 1;          /* For an error generation*/
 | 
						|
}
 | 
						|
 | 
						|
static size_t dynamic_column_offset_bytes_named(size_t data_length)
 | 
						|
{
 | 
						|
  if (data_length < 0xfff)                /* all 1 value is reserved */
 | 
						|
    return 2;
 | 
						|
  if (data_length < 0xfffff)              /* all 1 value is reserved */
 | 
						|
    return 3;
 | 
						|
  if (data_length < 0xfffffff)            /* all 1 value is reserved */
 | 
						|
    return 4;
 | 
						|
#if SIZEOF_SIZE_T > 4
 | 
						|
  if (data_length < 0xfffffffffull)       /* all 1 value is reserved */
 | 
						|
#endif
 | 
						|
    return 5;
 | 
						|
  return MAX_OFFSET_LENGTH_NM + 1;        /* For an error generation */
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Read offset and type information from index entry
 | 
						|
 | 
						|
  @param type            Where to put type info
 | 
						|
  @param offset          Where to put offset info
 | 
						|
  @param place           beginning of the type and offset
 | 
						|
  @param offset_size     Size of offset field in bytes
 | 
						|
*/
 | 
						|
 | 
						|
static my_bool type_and_offset_read_num(DYNAMIC_COLUMN_TYPE *type,
 | 
						|
                                        size_t *offset,
 | 
						|
                                        uchar *place, size_t offset_size)
 | 
						|
{
 | 
						|
  ulong UNINIT_VAR(val);
 | 
						|
  ulong UNINIT_VAR(lim);
 | 
						|
 | 
						|
  DBUG_ASSERT(offset_size >= 1 && offset_size <= 4);
 | 
						|
 | 
						|
  switch (offset_size) {
 | 
						|
  case 1:
 | 
						|
    val= (ulong)place[0];
 | 
						|
    lim= 0x1f;
 | 
						|
    break;
 | 
						|
  case 2:
 | 
						|
    val= uint2korr(place);
 | 
						|
    lim= 0x1fff;
 | 
						|
    break;
 | 
						|
  case 3:
 | 
						|
    val= uint3korr(place);
 | 
						|
    lim= 0x1fffff;
 | 
						|
    break;
 | 
						|
  case 4:
 | 
						|
    val= uint4korr(place);
 | 
						|
    lim= 0x1fffffff;
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    DBUG_ASSERT(0);                             /* impossible */
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
  *type= (val & 0x7) + 1;
 | 
						|
  *offset= val >> 3;
 | 
						|
  return (*offset >= lim);
 | 
						|
}
 | 
						|
 | 
						|
static my_bool type_and_offset_read_named(DYNAMIC_COLUMN_TYPE *type,
 | 
						|
                                          size_t *offset,
 | 
						|
                                          uchar *place, size_t offset_size)
 | 
						|
{
 | 
						|
  ulonglong UNINIT_VAR(val);
 | 
						|
  ulonglong UNINIT_VAR(lim);
 | 
						|
  DBUG_ASSERT(offset_size >= 2 && offset_size <= 5);
 | 
						|
 | 
						|
  switch (offset_size) {
 | 
						|
  case 2:
 | 
						|
    val= uint2korr(place);
 | 
						|
    lim= 0xfff;
 | 
						|
    break;
 | 
						|
  case 3:
 | 
						|
    val= uint3korr(place);
 | 
						|
    lim= 0xfffff;
 | 
						|
    break;
 | 
						|
  case 4:
 | 
						|
    val= uint4korr(place);
 | 
						|
    lim= 0xfffffff;
 | 
						|
    break;
 | 
						|
  case 5:
 | 
						|
    val= uint5korr(place);
 | 
						|
    lim= 0xfffffffffull;
 | 
						|
    break;
 | 
						|
  case 1:
 | 
						|
  default:
 | 
						|
    DBUG_ASSERT(0);                             /* impossible */
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
  *type= (val & 0xf) + 1;
 | 
						|
  *offset= (size_t) (val >> 4);
 | 
						|
  return (*offset >= lim);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Format descriptor, contain constants and function references for
 | 
						|
  format processing
 | 
						|
*/
 | 
						|
 | 
						|
struct st_service_funcs
 | 
						|
{
 | 
						|
  /* size of fixed header */
 | 
						|
  uint fixed_hdr;
 | 
						|
  /* size of fixed part of header entry */
 | 
						|
  uint fixed_hdr_entry;
 | 
						|
 | 
						|
  /*size of array element which stores keys */
 | 
						|
  uint key_size_in_array;
 | 
						|
 | 
						|
  /* Maximum data offset size in bytes */
 | 
						|
  size_t max_offset_size;
 | 
						|
 | 
						|
  size_t (*name_size)
 | 
						|
    (void *, uint);
 | 
						|
  int (*column_sort)
 | 
						|
    (const void *a, const void *b);
 | 
						|
  my_bool (*check_limit)
 | 
						|
    (const void *val);
 | 
						|
  void (*set_fixed_hdr)
 | 
						|
    (DYNAMIC_COLUMN *str, DYN_HEADER *hdr);
 | 
						|
  my_bool (*put_header_entry)(DYN_HEADER *hdr,
 | 
						|
                              void *column_key,
 | 
						|
                              DYNAMIC_COLUMN_VALUE *value,
 | 
						|
                              size_t offset);
 | 
						|
  int (*plan_sort)(const void *a, const void *b);
 | 
						|
  size_t (*dynamic_column_offset_bytes)(size_t data_length);
 | 
						|
  my_bool (*type_and_offset_read)(DYNAMIC_COLUMN_TYPE *type,
 | 
						|
                                  size_t *offset,
 | 
						|
                                  uchar *place, size_t offset_size);
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Actual our 2 format descriptors
 | 
						|
*/
 | 
						|
 | 
						|
static struct st_service_funcs fmt_data[2]=
 | 
						|
{
 | 
						|
  {
 | 
						|
    FIXED_HEADER_SIZE,
 | 
						|
    COLUMN_NUMBER_SIZE,
 | 
						|
    sizeof(uint),
 | 
						|
    MAX_OFFSET_LENGTH,
 | 
						|
    &name_size_num,
 | 
						|
    &column_sort_num,
 | 
						|
    &check_limit_num,
 | 
						|
    &set_fixed_header_num,
 | 
						|
    &put_header_entry_num,
 | 
						|
    &plan_sort_num,
 | 
						|
    &dynamic_column_offset_bytes_num,
 | 
						|
    &type_and_offset_read_num
 | 
						|
  },
 | 
						|
  {
 | 
						|
    FIXED_HEADER_SIZE_NM,
 | 
						|
    COLUMN_NAMEPTR_SIZE,
 | 
						|
    sizeof(LEX_STRING),
 | 
						|
    MAX_OFFSET_LENGTH_NM,
 | 
						|
    &name_size_named,
 | 
						|
    &column_sort_named,
 | 
						|
    &check_limit_named,
 | 
						|
    &set_fixed_header_named,
 | 
						|
    &put_header_entry_named,
 | 
						|
    &plan_sort_named,
 | 
						|
    &dynamic_column_offset_bytes_named,
 | 
						|
    &type_and_offset_read_named
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Read dynamic column record header and fill the descriptor
 | 
						|
 | 
						|
  @param hdr             dynamic columns record descriptor to fill
 | 
						|
  @param str             dynamic columns record
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
init_read_hdr(DYN_HEADER *hdr, DYNAMIC_COLUMN *str)
 | 
						|
{
 | 
						|
  if (read_fixed_header(hdr, str))
 | 
						|
    return ER_DYNCOL_FORMAT;
 | 
						|
  hdr->header= (uchar*)str->str + fmt_data[hdr->format].fixed_hdr;
 | 
						|
  calc_param(&hdr->entry_size, &hdr->header_size,
 | 
						|
             fmt_data[hdr->format].fixed_hdr_entry, hdr->offset_size,
 | 
						|
             hdr->column_count);
 | 
						|
  hdr->nmpool= hdr->header + hdr->header_size;
 | 
						|
  hdr->dtpool= hdr->nmpool + hdr->nmpool_size;
 | 
						|
  hdr->data_size= str->length - fmt_data[hdr->format].fixed_hdr -
 | 
						|
    hdr->header_size - hdr->nmpool_size;
 | 
						|
  hdr->data_end= (uchar*)str->str + str->length;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Initialize dynamic column string with (make it empty but correct format)
 | 
						|
 | 
						|
  @param str             The string to initialize
 | 
						|
  @param size            Amount of preallocated memory for the string.
 | 
						|
 | 
						|
  @retval FALSE OK
 | 
						|
  @retval TRUE  error
 | 
						|
*/
 | 
						|
 | 
						|
static my_bool dynamic_column_init_named(DYNAMIC_COLUMN *str, size_t size)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(size != 0);
 | 
						|
 | 
						|
  /*
 | 
						|
    Make string with no fields (empty header)
 | 
						|
    - First \0 is flags
 | 
						|
    - other 2 \0 is number of fields
 | 
						|
  */
 | 
						|
  if (init_dynamic_string(str, NULL, size, DYNCOL_SYZERESERVE))
 | 
						|
    return TRUE;
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate how many bytes needed to store val as variable length integer
 | 
						|
  where first bit indicate continuation of the sequence.
 | 
						|
 | 
						|
  @param val             The value for which we are calculating length
 | 
						|
 | 
						|
  @return number of bytes
 | 
						|
*/
 | 
						|
 | 
						|
static size_t dynamic_column_var_uint_bytes(ulonglong val)
 | 
						|
{
 | 
						|
  size_t len= 0;
 | 
						|
  do
 | 
						|
  {
 | 
						|
    len++;
 | 
						|
    val>>= 7;
 | 
						|
  } while (val);
 | 
						|
  return len;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
   Stores variable length unsigned integer value to a string
 | 
						|
 | 
						|
  @param str             The string where to append the value
 | 
						|
  @param val             The value to put in the string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
 | 
						|
  @notes
 | 
						|
  This is used to store a number together with other data in the same
 | 
						|
  object.  (Like decimals, length of string etc)
 | 
						|
  (As we don't know the length of this object, we can't store 0 in 0 bytes)
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_var_uint_store(DYNAMIC_COLUMN *str, ulonglong val)
 | 
						|
{
 | 
						|
  if (dynstr_realloc(str, 10))                  /* max what we can use */
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
 | 
						|
  do
 | 
						|
  {
 | 
						|
    ulonglong rest= val >> 7;
 | 
						|
    str->str[str->length++]= ((val & 0x7f) | (rest ? 0x80 : 0x00));
 | 
						|
    val= rest;
 | 
						|
  } while (val);
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Reads variable length unsigned integer value from a string
 | 
						|
 | 
						|
  @param data            The string from which the int should be read
 | 
						|
  @param data_length	 Max length of data
 | 
						|
  @param len             Where to put length of the string read in bytes
 | 
						|
 | 
						|
  @return value of the unsigned integer read from the string
 | 
						|
 | 
						|
  In case of error, *len is set to 0
 | 
						|
*/
 | 
						|
 | 
						|
static ulonglong
 | 
						|
dynamic_column_var_uint_get(uchar *data, size_t data_length,
 | 
						|
                            size_t *len)
 | 
						|
{
 | 
						|
  ulonglong val= 0;
 | 
						|
  uint length;
 | 
						|
  uchar *end= data + data_length;
 | 
						|
 | 
						|
  for (length=0; data < end ; data++)
 | 
						|
  {
 | 
						|
    val+= (((ulonglong)((*data) & 0x7f)) << (length * 7));
 | 
						|
    length++;
 | 
						|
    if (!((*data) & 0x80))
 | 
						|
    {
 | 
						|
      /* End of data */
 | 
						|
      *len= length;
 | 
						|
      return val;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  /* Something was wrong with data */
 | 
						|
  *len= 0;                                      /* Mark error */
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate how many bytes needed to store val as unsigned.
 | 
						|
 | 
						|
  @param val             The value for which we are calculating length
 | 
						|
 | 
						|
  @return number of bytes (0-8)
 | 
						|
*/
 | 
						|
 | 
						|
static size_t dynamic_column_uint_bytes(ulonglong val)
 | 
						|
{
 | 
						|
  size_t len;
 | 
						|
 | 
						|
  for (len= 0; val ; val>>= 8, len++)
 | 
						|
    ;
 | 
						|
  return len;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Append the string with given unsigned int value.
 | 
						|
 | 
						|
  @param str             The string where to put the value
 | 
						|
  @param val             The value to put in the string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_uint_store(DYNAMIC_COLUMN *str, ulonglong val)
 | 
						|
{
 | 
						|
  if (dynstr_realloc(str, 8)) /* max what we can use */
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
 | 
						|
  for (; val; val>>= 8)
 | 
						|
    str->str[str->length++]= (char) (val & 0xff);
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Read unsigned int value of given length from the string
 | 
						|
 | 
						|
  @param store_it_here   The structure to store the value
 | 
						|
  @param data            The string which should be read
 | 
						|
  @param length          The length (in bytes) of the value in nthe string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_uint_read(DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                         uchar *data, size_t length)
 | 
						|
{
 | 
						|
  ulonglong value= 0;
 | 
						|
  size_t i;
 | 
						|
 | 
						|
  for (i= 0; i < length; i++)
 | 
						|
    value+= ((ulonglong)data[i]) << (i*8);
 | 
						|
 | 
						|
  store_it_here->x.ulong_value= value;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate how many bytes needed to store val as signed in following encoding:
 | 
						|
    0 -> 0
 | 
						|
   -1 -> 1
 | 
						|
    1 -> 2
 | 
						|
   -2 -> 3
 | 
						|
    2 -> 4
 | 
						|
   ...
 | 
						|
 | 
						|
  @param val             The value for which we are calculating length
 | 
						|
 | 
						|
  @return number of bytes
 | 
						|
*/
 | 
						|
 | 
						|
static size_t dynamic_column_sint_bytes(longlong val)
 | 
						|
{
 | 
						|
  return dynamic_column_uint_bytes((((ulonglong) val) << 1) ^
 | 
						|
                                   (val < 0 ? 0xffffffffffffffffull : 0));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Append the string with given signed int value.
 | 
						|
 | 
						|
  @param str             the string where to put the value
 | 
						|
  @param val             the value to put in the string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_sint_store(DYNAMIC_COLUMN *str, longlong val)
 | 
						|
{
 | 
						|
  return dynamic_column_uint_store(str,
 | 
						|
                                   (((ulonglong) val) << 1) ^
 | 
						|
                                   (val < 0 ? 0xffffffffffffffffULL : 0));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Read signed int value of given length from the string
 | 
						|
 | 
						|
  @param store_it_here   The structure to store the value
 | 
						|
  @param data            The string which should be read
 | 
						|
  @param length          The length (in bytes) of the value in the string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_sint_read(DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                         uchar *data, size_t length)
 | 
						|
{
 | 
						|
  ulonglong val;
 | 
						|
  dynamic_column_uint_read(store_it_here, data, length);
 | 
						|
  val= store_it_here->x.ulong_value;
 | 
						|
  if (val & 1)
 | 
						|
    val= (val >> 1) ^ 0xffffffffffffffffULL;
 | 
						|
  else
 | 
						|
    val>>= 1;
 | 
						|
  store_it_here->x.long_value= (longlong) val;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate how many bytes needed to store the value.
 | 
						|
 | 
						|
  @param value          The value for which we are calculating length
 | 
						|
 | 
						|
  @return
 | 
						|
  Error:  (size_t) ~0
 | 
						|
  ok      number of bytes
 | 
						|
*/
 | 
						|
 | 
						|
static size_t
 | 
						|
dynamic_column_value_len(DYNAMIC_COLUMN_VALUE *value,
 | 
						|
                         enum enum_dyncol_format format)
 | 
						|
{
 | 
						|
  switch (value->type) {
 | 
						|
  case DYN_COL_NULL:
 | 
						|
    return 0;
 | 
						|
  case DYN_COL_INT:
 | 
						|
    return dynamic_column_sint_bytes(value->x.long_value);
 | 
						|
  case DYN_COL_UINT:
 | 
						|
    return dynamic_column_uint_bytes(value->x.ulong_value);
 | 
						|
  case DYN_COL_DOUBLE:
 | 
						|
    return 8;
 | 
						|
  case DYN_COL_STRING:
 | 
						|
    return (dynamic_column_var_uint_bytes(value->x.string.charset->number) +
 | 
						|
            value->x.string.value.length);
 | 
						|
  case DYN_COL_DECIMAL:
 | 
						|
  {
 | 
						|
    int precision= value->x.decimal.value.intg + value->x.decimal.value.frac;
 | 
						|
    int scale= value->x.decimal.value.frac;
 | 
						|
 | 
						|
    if (precision == 0 || decimal_is_zero(&value->x.decimal.value))
 | 
						|
    {
 | 
						|
      /* This is here to simplify dynamic_column_decimal_store() */
 | 
						|
      value->x.decimal.value.intg= value->x.decimal.value.frac= 0;
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
    /*
 | 
						|
      Check if legal decimal;  This is needed to not get an assert in
 | 
						|
      decimal_bin_size(). However this should be impossible as all
 | 
						|
      decimals entered here should be valid and we have the special check
 | 
						|
      above to handle the unlikely but possible case that decimal.value.intg
 | 
						|
      and decimal.frac is 0.
 | 
						|
    */
 | 
						|
    if (scale < 0 || precision <= 0)
 | 
						|
    {
 | 
						|
      DBUG_ASSERT(0);                           /* Impossible */
 | 
						|
      return (size_t) ~0;
 | 
						|
    }
 | 
						|
    return (dynamic_column_var_uint_bytes(value->x.decimal.value.intg) +
 | 
						|
            dynamic_column_var_uint_bytes(value->x.decimal.value.frac) +
 | 
						|
            decimal_bin_size(precision, scale));
 | 
						|
  }
 | 
						|
  case DYN_COL_DATETIME:
 | 
						|
    if (format == dyncol_fmt_num || value->x.time_value.second_part)
 | 
						|
      /* date+time in bits: 14 + 4 + 5 + 10 + 6 + 6 + 20 + 1 66bits ~= 9 bytes*/
 | 
						|
      return 9;
 | 
						|
    else
 | 
						|
      return 6;
 | 
						|
  case DYN_COL_DATE:
 | 
						|
    /* date in dits: 14 + 4 + 5 = 23bits ~= 3bytes*/
 | 
						|
    return 3;
 | 
						|
  case DYN_COL_TIME:
 | 
						|
    if (format == dyncol_fmt_num || value->x.time_value.second_part)
 | 
						|
      /* time in bits: 10 + 6 + 6 + 20 + 1 = 43bits ~= 6bytes*/
 | 
						|
      return 6;
 | 
						|
    else
 | 
						|
      return 3;
 | 
						|
  case DYN_COL_DYNCOL:
 | 
						|
    return value->x.string.value.length;
 | 
						|
  }
 | 
						|
  DBUG_ASSERT(0);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Append double value to a string
 | 
						|
 | 
						|
  @param str             the string where to put the value
 | 
						|
  @param val             the value to put in the string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_double_store(DYNAMIC_COLUMN *str, double val)
 | 
						|
{
 | 
						|
   if (dynstr_realloc(str, 8))
 | 
						|
     return ER_DYNCOL_RESOURCE;
 | 
						|
   float8store(str->str + str->length, val);
 | 
						|
   str->length+= 8;
 | 
						|
   return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Read double value of given length from the string
 | 
						|
 | 
						|
  @param store_it_here   The structure to store the value
 | 
						|
  @param data            The string which should be read
 | 
						|
  @param length          The length (in bytes) of the value in nthe string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_double_read(DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                               uchar *data, size_t length)
 | 
						|
{
 | 
						|
  if (length != 8)
 | 
						|
    return ER_DYNCOL_FORMAT;
 | 
						|
  float8get(store_it_here->x.double_value, data);
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Append the string with given string value.
 | 
						|
 | 
						|
  @param str             the string where to put the value
 | 
						|
  @param val             the value to put in the string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_string_store(DYNAMIC_COLUMN *str, LEX_STRING *string,
 | 
						|
                            CHARSET_INFO *charset)
 | 
						|
{
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
  if ((rc= dynamic_column_var_uint_store(str, charset->number)))
 | 
						|
    return rc;
 | 
						|
  if (dynstr_append_mem(str, string->str, string->length))
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Append the string with given string value.
 | 
						|
 | 
						|
  @param str             the string where to put the value
 | 
						|
  @param val             the value to put in the string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_dyncol_store(DYNAMIC_COLUMN *str, LEX_STRING *string)
 | 
						|
{
 | 
						|
  if (dynstr_append_mem(str, string->str, string->length))
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Read string value of given length from the packed string
 | 
						|
 | 
						|
  @param store_it_here   The structure to store the value
 | 
						|
  @param data            The packed string which should be read
 | 
						|
  @param length          The length (in bytes) of the value in nthe string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_string_read(DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                           uchar *data, size_t length)
 | 
						|
{
 | 
						|
  size_t len;
 | 
						|
  uint charset_nr= (uint)dynamic_column_var_uint_get(data, length, &len);
 | 
						|
  if (len == 0)                                /* Wrong packed number */
 | 
						|
    return ER_DYNCOL_FORMAT;
 | 
						|
  store_it_here->x.string.charset= get_charset(charset_nr, MYF(MY_WME));
 | 
						|
  if (store_it_here->x.string.charset == NULL)
 | 
						|
    return ER_DYNCOL_UNKNOWN_CHARSET;
 | 
						|
  data+= len;
 | 
						|
  store_it_here->x.string.value.length= (length-= len);
 | 
						|
  store_it_here->x.string.value.str= (char*) data;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Read Dynamic columns packet string value of given length
 | 
						|
  from the packed string
 | 
						|
 | 
						|
  @param store_it_here   The structure to store the value
 | 
						|
  @param data            The packed string which should be read
 | 
						|
  @param length          The length (in bytes) of the value in nthe string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_dyncol_read(DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                           uchar *data, size_t length)
 | 
						|
{
 | 
						|
  store_it_here->x.string.charset= &my_charset_bin;
 | 
						|
  store_it_here->x.string.value.length= length;
 | 
						|
  store_it_here->x.string.value.str= (char*) data;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Append the string with given decimal value.
 | 
						|
 | 
						|
  @param str             the string where to put the value
 | 
						|
  @param val             the value to put in the string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_decimal_store(DYNAMIC_COLUMN *str,
 | 
						|
                             decimal_t *value)
 | 
						|
{
 | 
						|
  uint bin_size;
 | 
						|
  int precision= value->intg + value->frac;
 | 
						|
  
 | 
						|
  /* Store decimal zero as empty string */
 | 
						|
  if (precision == 0)
 | 
						|
    return ER_DYNCOL_OK;
 | 
						|
 | 
						|
  bin_size= decimal_bin_size(precision, value->frac);
 | 
						|
  if (dynstr_realloc(str, bin_size + 20))
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
 | 
						|
  /* The following can't fail as memory is already allocated */
 | 
						|
  (void) dynamic_column_var_uint_store(str, value->intg);
 | 
						|
  (void) dynamic_column_var_uint_store(str, value->frac);
 | 
						|
 | 
						|
  decimal2bin(value, (uchar *) str->str + str->length,
 | 
						|
              precision, value->frac);
 | 
						|
  str->length+= bin_size;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Prepare the value to be used as decimal.
 | 
						|
 | 
						|
  @param value           The value structure which sould be setup.
 | 
						|
*/
 | 
						|
 | 
						|
void mariadb_dyncol_prepare_decimal(DYNAMIC_COLUMN_VALUE *value)
 | 
						|
{
 | 
						|
  value->x.decimal.value.buf= value->x.decimal.buffer;
 | 
						|
  value->x.decimal.value.len= DECIMAL_BUFF_LENGTH;
 | 
						|
  /* just to be safe */
 | 
						|
  value->type= DYN_COL_DECIMAL;
 | 
						|
  decimal_make_zero(&value->x.decimal.value);
 | 
						|
}
 | 
						|
 | 
						|
void dynamic_column_prepare_decimal(DYNAMIC_COLUMN_VALUE *value)
 | 
						|
{
 | 
						|
  mariadb_dyncol_prepare_decimal(value);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Read decimal value of given length from the string
 | 
						|
 | 
						|
  @param store_it_here   The structure to store the value
 | 
						|
  @param data            The string which should be read
 | 
						|
  @param length          The length (in bytes) of the value in nthe string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_decimal_read(DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                            uchar *data, size_t length)
 | 
						|
{
 | 
						|
  size_t intg_len, frac_len;
 | 
						|
  int intg, frac, precision, scale;
 | 
						|
 | 
						|
  dynamic_column_prepare_decimal(store_it_here);
 | 
						|
  /* Decimals 0.0 is stored as a zero length string */
 | 
						|
  if (length == 0)
 | 
						|
    return ER_DYNCOL_OK;                        /* value contains zero */
 | 
						|
 | 
						|
  intg= (int)dynamic_column_var_uint_get(data, length, &intg_len);
 | 
						|
  data+= intg_len;
 | 
						|
  frac= (int)dynamic_column_var_uint_get(data, length - intg_len, &frac_len);
 | 
						|
  data+= frac_len;
 | 
						|
 | 
						|
  /* Check the size of data is correct */
 | 
						|
  precision= intg + frac;
 | 
						|
  scale=     frac;
 | 
						|
  if (scale < 0 || precision <= 0 || scale > precision ||
 | 
						|
      (length - intg_len - frac_len) >
 | 
						|
      (size_t) (DECIMAL_BUFF_LENGTH*sizeof(decimal_digit_t)) ||
 | 
						|
      decimal_bin_size(intg + frac, frac) !=
 | 
						|
      (uint) (length - intg_len - frac_len))
 | 
						|
    return ER_DYNCOL_FORMAT;
 | 
						|
 | 
						|
  if (bin2decimal(data, &store_it_here->x.decimal.value, precision, scale) !=
 | 
						|
      E_DEC_OK)
 | 
						|
    return ER_DYNCOL_FORMAT;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Append the string with given datetime value.
 | 
						|
 | 
						|
  @param str             the string where to put the value
 | 
						|
  @param value           the value to put in the string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_date_time_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value,
 | 
						|
                               enum enum_dyncol_format format)
 | 
						|
{
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
  /*
 | 
						|
    0<----year----><mn><day>00000!<-hours--><min-><sec-><---microseconds--->
 | 
						|
     12345678901234123412345     1123456789012345612345612345678901234567890
 | 
						|
    <123456><123456><123456><123456><123456><123456><123456><123456><123456>
 | 
						|
  */
 | 
						|
  if ((rc= dynamic_column_date_store(str, value)) ||
 | 
						|
      (rc= dynamic_column_time_store(str, value, format)))
 | 
						|
    return rc;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Read datetime value of given length from the packed string
 | 
						|
 | 
						|
  @param store_it_here   The structure to store the value
 | 
						|
  @param data            The packed string which should be read
 | 
						|
  @param length          The length (in bytes) of the value in nthe string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_date_time_read(DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                              uchar *data, size_t length)
 | 
						|
{
 | 
						|
  enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT;
 | 
						|
  /*
 | 
						|
    0<----year----><mn><day>00000!<-hours--><min-><sec-><---microseconds--->
 | 
						|
     12345678901234123412345     1123456789012345612345612345678901234567890
 | 
						|
    <123456><123456><123456><123456><123456><123456><123456><123456><123456>
 | 
						|
  */
 | 
						|
  if (length != 9 && length != 6)
 | 
						|
    goto err;
 | 
						|
  store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_DATETIME;
 | 
						|
  if ((rc= dynamic_column_date_read_internal(store_it_here, data, 3)) ||
 | 
						|
      (rc= dynamic_column_time_read_internal(store_it_here, data + 3,
 | 
						|
                                             length - 3)))
 | 
						|
    goto err;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
 | 
						|
err:
 | 
						|
  store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_ERROR;
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Append the string with given time value.
 | 
						|
 | 
						|
  @param str             the string where to put the value
 | 
						|
  @param value           the value to put in the string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_time_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value,
 | 
						|
                          enum enum_dyncol_format format)
 | 
						|
{
 | 
						|
  uchar *buf;
 | 
						|
  if (dynstr_realloc(str, 6))
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
 | 
						|
  buf= ((uchar *)str->str) + str->length;
 | 
						|
 | 
						|
  if (value->time_type == MYSQL_TIMESTAMP_NONE ||
 | 
						|
      value->time_type == MYSQL_TIMESTAMP_ERROR ||
 | 
						|
      value->time_type == MYSQL_TIMESTAMP_DATE)
 | 
						|
  {
 | 
						|
    value->neg= 0;
 | 
						|
    value->second_part= 0;
 | 
						|
    value->hour= 0;
 | 
						|
    value->minute= 0;
 | 
						|
    value->second= 0;
 | 
						|
  }
 | 
						|
  DBUG_ASSERT(value->hour <= 838);
 | 
						|
  DBUG_ASSERT(value->minute <= 59);
 | 
						|
  DBUG_ASSERT(value->second <= 59);
 | 
						|
  DBUG_ASSERT(value->second_part <= 999999);
 | 
						|
  if (format == dyncol_fmt_num || value->second_part)
 | 
						|
  {
 | 
						|
  /*
 | 
						|
    00000!<-hours--><min-><sec-><---microseconds--->
 | 
						|
         1123456789012345612345612345678901234567890
 | 
						|
    <123456><123456><123456><123456><123456><123456>
 | 
						|
  */
 | 
						|
    buf[0]= (value->second_part & 0xff);
 | 
						|
    buf[1]= ((value->second_part & 0xff00) >> 8);
 | 
						|
    buf[2]= (uchar)(((value->second & 0xf) << 4) |
 | 
						|
                    ((value->second_part & 0xf0000) >> 16));
 | 
						|
    buf[3]= ((value->minute << 2) | ((value->second & 0x30) >> 4));
 | 
						|
    buf[4]= (value->hour & 0xff);
 | 
						|
    buf[5]= ((value->neg ? 0x4 : 0) | (value->hour >> 8));
 | 
						|
    str->length+= 6;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
  /*
 | 
						|
     !<-hours--><min-><sec->
 | 
						|
     11234567890123456123456
 | 
						|
    <123456><123456><123456>
 | 
						|
  */
 | 
						|
    buf[0]= (value->second) | ((value->minute & 0x3) << 6);
 | 
						|
    buf[1]= (value->minute >> 2) | ((value->hour & 0xf) << 4);
 | 
						|
    buf[2]= (value->hour >> 4) | (value->neg ? 0x80 : 0);
 | 
						|
    str->length+= 3;
 | 
						|
  }
 | 
						|
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Read time value of given length from the packed string
 | 
						|
 | 
						|
  @param store_it_here   The structure to store the value
 | 
						|
  @param data            The packed string which should be read
 | 
						|
  @param length          The length (in bytes) of the value in nthe string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_time_read(DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                         uchar *data, size_t length)
 | 
						|
{
 | 
						|
  store_it_here->x.time_value.year= store_it_here->x.time_value.month=
 | 
						|
    store_it_here->x.time_value.day= 0;
 | 
						|
  store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_TIME;
 | 
						|
  return dynamic_column_time_read_internal(store_it_here, data, length);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Internal function for reading time part from the string.
 | 
						|
 | 
						|
  @param store_it_here   The structure to store the value
 | 
						|
  @param data            The packed string which should be read
 | 
						|
  @param length          The length (in bytes) of the value in nthe string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_time_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                                  uchar *data, size_t length)
 | 
						|
{
 | 
						|
  if (length != 6 && length != 3)
 | 
						|
    goto err;
 | 
						|
  if (length == 6)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      00000!<-hours--><min-><sec-><---microseconds--->
 | 
						|
      1123456789012345612345612345678901234567890
 | 
						|
      <123456><123456><123456><123456><123456><123456>
 | 
						|
    */
 | 
						|
    store_it_here->x.time_value.second_part= (data[0] |
 | 
						|
                                              (data[1] << 8) |
 | 
						|
                                              ((data[2] & 0xf) << 16));
 | 
						|
    store_it_here->x.time_value.second= ((data[2] >> 4) |
 | 
						|
                                         ((data[3] & 0x3) << 4));
 | 
						|
    store_it_here->x.time_value.minute= (data[3] >> 2);
 | 
						|
    store_it_here->x.time_value.hour= (((((uint)data[5]) & 0x3 ) << 8) | data[4]);
 | 
						|
    store_it_here->x.time_value.neg= ((data[5] & 0x4) ? 1 : 0);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    /*
 | 
						|
     !<-hours--><min-><sec->
 | 
						|
     11234567890123456123456
 | 
						|
    <123456><123456><123456>
 | 
						|
   */
 | 
						|
    store_it_here->x.time_value.second_part= 0;
 | 
						|
    store_it_here->x.time_value.second= (data[0] & 0x3f);
 | 
						|
    store_it_here->x.time_value.minute= (data[0] >> 6) | ((data[1] & 0xf) << 2);
 | 
						|
    store_it_here->x.time_value.hour= (data[1] >> 4) | ((data[2] & 0x3f) << 4);
 | 
						|
    store_it_here->x.time_value.neg= ((data[2] & 0x80) ? 1 : 0);
 | 
						|
  }
 | 
						|
  if (store_it_here->x.time_value.second > 59 ||
 | 
						|
      store_it_here->x.time_value.minute > 59 ||
 | 
						|
      store_it_here->x.time_value.hour > 838 ||
 | 
						|
      store_it_here->x.time_value.second_part > 999999)
 | 
						|
    goto err;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
 | 
						|
err:
 | 
						|
  store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_ERROR;
 | 
						|
  return ER_DYNCOL_FORMAT;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Append the string with given date value.
 | 
						|
 | 
						|
  @param str             the string where to put the value
 | 
						|
  @param value           the value to put in the string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_date_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value)
 | 
						|
{
 | 
						|
  uchar *buf;
 | 
						|
  if (dynstr_realloc(str, 3))
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
 | 
						|
  buf= ((uchar *)str->str) + str->length;
 | 
						|
  if (value->time_type == MYSQL_TIMESTAMP_NONE ||
 | 
						|
      value->time_type == MYSQL_TIMESTAMP_ERROR ||
 | 
						|
      value->time_type == MYSQL_TIMESTAMP_TIME)
 | 
						|
    value->year= value->month= value->day = 0;
 | 
						|
  DBUG_ASSERT(value->year <= 9999);
 | 
						|
  DBUG_ASSERT(value->month <= 12);
 | 
						|
  DBUG_ASSERT(value->day <= 31);
 | 
						|
  /*
 | 
						|
    0<----year----><mn><day>
 | 
						|
    012345678901234123412345
 | 
						|
    <123456><123456><123456>
 | 
						|
  */
 | 
						|
  buf[0]= (value->day |
 | 
						|
           ((value->month & 0x7) << 5));
 | 
						|
  buf[1]= ((value->month >> 3) | ((value->year & 0x7F) << 1));
 | 
						|
  buf[2]= (value->year >> 7);
 | 
						|
  str->length+= 3;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Read date value of given length from the packed string
 | 
						|
 | 
						|
  @param store_it_here   The structure to store the value
 | 
						|
  @param data            The packed string which should be read
 | 
						|
  @param length          The length (in bytes) of the value in nthe string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_date_read(DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                         uchar *data, size_t length)
 | 
						|
{
 | 
						|
  store_it_here->x.time_value.neg= 0;
 | 
						|
  store_it_here->x.time_value.second_part= 0;
 | 
						|
  store_it_here->x.time_value.hour= 0;
 | 
						|
  store_it_here->x.time_value.minute= 0;
 | 
						|
  store_it_here->x.time_value.second= 0;
 | 
						|
  store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_DATE;
 | 
						|
  return dynamic_column_date_read_internal(store_it_here, data, length);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Internal function for reading date part from the string.
 | 
						|
 | 
						|
  @param store_it_here   The structure to store the value
 | 
						|
  @param data            The packed string which should be read
 | 
						|
  @param length          The length (in bytes) of the value in nthe string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_date_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                                  uchar *data,
 | 
						|
                                  size_t length)
 | 
						|
{
 | 
						|
  if (length != 3)
 | 
						|
    goto err;
 | 
						|
  /*
 | 
						|
    0<----year----><mn><day>
 | 
						|
     12345678901234123412345
 | 
						|
    <123456><123456><123456>
 | 
						|
  */
 | 
						|
  store_it_here->x.time_value.day= (data[0] & 0x1f);
 | 
						|
  store_it_here->x.time_value.month= (((data[1] & 0x1) << 3) |
 | 
						|
                                    (data[0] >> 5));
 | 
						|
  store_it_here->x.time_value.year= ((((uint)data[2]) << 7) |
 | 
						|
                                    (data[1] >> 1));
 | 
						|
  if (store_it_here->x.time_value.day > 31 ||
 | 
						|
      store_it_here->x.time_value.month > 12 ||
 | 
						|
      store_it_here->x.time_value.year > 9999)
 | 
						|
    goto err;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
 | 
						|
err:
 | 
						|
  store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_ERROR;
 | 
						|
  return ER_DYNCOL_FORMAT;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Append the string with given value.
 | 
						|
 | 
						|
  @param str             the string where to put the value
 | 
						|
  @param value           the value to put in the string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
data_store(DYNAMIC_COLUMN *str, DYNAMIC_COLUMN_VALUE *value,
 | 
						|
           enum enum_dyncol_format format)
 | 
						|
{
 | 
						|
  switch (value->type) {
 | 
						|
  case DYN_COL_INT:
 | 
						|
    return dynamic_column_sint_store(str, value->x.long_value);
 | 
						|
  case DYN_COL_UINT:
 | 
						|
    return dynamic_column_uint_store(str, value->x.ulong_value);
 | 
						|
  case DYN_COL_DOUBLE:
 | 
						|
    return dynamic_column_double_store(str, value->x.double_value);
 | 
						|
  case DYN_COL_STRING:
 | 
						|
    return dynamic_column_string_store(str, &value->x.string.value,
 | 
						|
                                     value->x.string.charset);
 | 
						|
  case DYN_COL_DECIMAL:
 | 
						|
    return dynamic_column_decimal_store(str, &value->x.decimal.value);
 | 
						|
  case DYN_COL_DATETIME:
 | 
						|
    /* date+time in bits: 14 + 4 + 5 + 5 + 6 + 6 40bits = 5 bytes */
 | 
						|
    return dynamic_column_date_time_store(str, &value->x.time_value, format);
 | 
						|
  case DYN_COL_DATE:
 | 
						|
    /* date in dits: 14 + 4 + 5 = 23bits ~= 3bytes*/
 | 
						|
    return dynamic_column_date_store(str, &value->x.time_value);
 | 
						|
  case DYN_COL_TIME:
 | 
						|
    /* time in bits: 5 + 6 + 6 = 17bits ~= 3bytes*/
 | 
						|
    return dynamic_column_time_store(str, &value->x.time_value, format);
 | 
						|
  case DYN_COL_DYNCOL:
 | 
						|
    return dynamic_column_dyncol_store(str, &value->x.string.value);
 | 
						|
  case DYN_COL_NULL:
 | 
						|
    break;                                      /* Impossible */
 | 
						|
  }
 | 
						|
  DBUG_ASSERT(0);
 | 
						|
  return ER_DYNCOL_OK;                          /* Impossible */
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Write information to the fixed header
 | 
						|
 | 
						|
  @param str             String where to write the header
 | 
						|
  @param offset_size     Size of offset field in bytes
 | 
						|
  @param column_count    Number of columns
 | 
						|
*/
 | 
						|
 | 
						|
static void set_fixed_header(DYNAMIC_COLUMN *str,
 | 
						|
                             uint offset_size,
 | 
						|
                             uint column_count)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(column_count <= 0xffff);
 | 
						|
  DBUG_ASSERT(offset_size <= MAX_OFFSET_LENGTH);
 | 
						|
  str->str[0]= ((str->str[0] & ~DYNCOL_FLG_OFFSET) |
 | 
						|
                (offset_size - 1));             /* size of offset */
 | 
						|
  int2store(str->str + 1, column_count);        /* columns number */
 | 
						|
  DBUG_ASSERT((str->str[0] & (~DYNCOL_FLG_KNOWN)) == 0);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Adds columns into the empty string
 | 
						|
 | 
						|
  @param str             String where to write the data (the record)
 | 
						|
  @param hdr             Dynamic columns record descriptor
 | 
						|
  @param column_count    Number of columns in the arrays
 | 
						|
  @param column_keys     Array of columns keys (uint or LEX_STRING)
 | 
						|
  @param values          Array of columns values
 | 
						|
  @param new_str         True if we need to allocate new string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_new_column_store(DYNAMIC_COLUMN *str,
 | 
						|
                         DYN_HEADER *hdr,
 | 
						|
                         uint column_count,
 | 
						|
                         void *column_keys,
 | 
						|
                         DYNAMIC_COLUMN_VALUE *values,
 | 
						|
                         my_bool new_str)
 | 
						|
{
 | 
						|
  struct st_service_funcs *fmt= fmt_data + hdr->format;
 | 
						|
  void **UNINIT_VAR(columns_order);
 | 
						|
  uchar *element;
 | 
						|
  uint i;
 | 
						|
  enum enum_dyncol_func_result rc= ER_DYNCOL_RESOURCE;
 | 
						|
  size_t all_headers_size;
 | 
						|
 | 
						|
  if (column_count && !(columns_order= malloc(sizeof(void*)*column_count)))
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
  if (new_str || str->str == 0)
 | 
						|
  {
 | 
						|
    if (column_count)
 | 
						|
    {
 | 
						|
      if (dynamic_column_init_named(str,
 | 
						|
                                    fmt->fixed_hdr +
 | 
						|
                                    hdr->header_size +
 | 
						|
                                    hdr->nmpool_size +
 | 
						|
                                    hdr->data_size +
 | 
						|
                                    DYNCOL_SYZERESERVE))
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      mariadb_dyncol_init(str);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    str->length= 0;
 | 
						|
    if (dynstr_realloc(str,
 | 
						|
                       fmt->fixed_hdr +
 | 
						|
                       hdr->header_size +
 | 
						|
                       hdr->nmpool_size +
 | 
						|
                       hdr->data_size +
 | 
						|
                       DYNCOL_SYZERESERVE))
 | 
						|
      goto err;
 | 
						|
  }
 | 
						|
  if (!column_count)
 | 
						|
    return ER_DYNCOL_OK;
 | 
						|
 | 
						|
  bzero(str->str, fmt->fixed_hdr);
 | 
						|
  str->length= fmt->fixed_hdr;
 | 
						|
 | 
						|
  /* sort columns for the header */
 | 
						|
  for (i= 0, element= (uchar *) column_keys;
 | 
						|
       i < column_count;
 | 
						|
       i++, element+= fmt->key_size_in_array)
 | 
						|
    columns_order[i]= (void *)element;
 | 
						|
  qsort(columns_order, (size_t)column_count, sizeof(void*), fmt->column_sort);
 | 
						|
 | 
						|
  /*
 | 
						|
    For now we don't allow creating two columns with the same number
 | 
						|
    at the time of create.  This can be fixed later to just use the later
 | 
						|
    by comparing the pointers.
 | 
						|
  */
 | 
						|
  for (i= 0; i < column_count - 1; i++)
 | 
						|
  {
 | 
						|
    if ((*fmt->check_limit)(&columns_order[i]) ||
 | 
						|
        (*fmt->column_sort)(&columns_order[i], &columns_order[i + 1]) == 0)
 | 
						|
    {
 | 
						|
      rc= ER_DYNCOL_DATA;
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if ((*fmt->check_limit)(&columns_order[i]))
 | 
						|
  {
 | 
						|
    rc= ER_DYNCOL_DATA;
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
 | 
						|
  (*fmt->set_fixed_hdr)(str, hdr);
 | 
						|
  /* reserve place for header and name pool */
 | 
						|
  str->length+= hdr->header_size + hdr->nmpool_size;
 | 
						|
 | 
						|
  hdr->entry= hdr->header;
 | 
						|
  hdr->name= hdr->nmpool;
 | 
						|
  all_headers_size= fmt->fixed_hdr + hdr->header_size + hdr->nmpool_size;
 | 
						|
  for (i= 0; i < column_count; i++)
 | 
						|
  {
 | 
						|
    uint ord= (uint)(((uchar*)columns_order[i] - (uchar*)column_keys) /
 | 
						|
                     fmt->key_size_in_array);
 | 
						|
    if (values[ord].type != DYN_COL_NULL)
 | 
						|
    {
 | 
						|
      /* Store header first in the str */
 | 
						|
      if ((*fmt->put_header_entry)(hdr, columns_order[i], values + ord,
 | 
						|
                                   str->length - all_headers_size))
 | 
						|
      {
 | 
						|
        rc= ER_DYNCOL_FORMAT;
 | 
						|
        goto err;
 | 
						|
      }
 | 
						|
 | 
						|
      /* Store value in 'str + str->length' and increase str->length */
 | 
						|
      if ((rc= data_store(str, values + ord, hdr->format)))
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  rc= ER_DYNCOL_OK;
 | 
						|
err:
 | 
						|
  free(columns_order);
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate size of header, name pool and data pool
 | 
						|
 | 
						|
  @param hdr             descriptor of dynamic column record
 | 
						|
  @param column_count    number of elements in arrays
 | 
						|
  @param column_count    Number of columns in the arrays
 | 
						|
  @param column_keys     Array of columns keys (uint or LEX_STRING)
 | 
						|
  @param values          Array of columns values
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
calc_var_sizes(DYN_HEADER *hdr,
 | 
						|
               uint column_count,
 | 
						|
               void *column_keys,
 | 
						|
               DYNAMIC_COLUMN_VALUE *values)
 | 
						|
{
 | 
						|
  struct st_service_funcs *fmt= fmt_data + hdr->format;
 | 
						|
  uint i;
 | 
						|
  hdr->nmpool_size= hdr->data_size= 0;
 | 
						|
  hdr->column_count= 0;
 | 
						|
  for (i= 0; i < column_count; i++)
 | 
						|
  {
 | 
						|
    if (values[i].type != DYN_COL_NULL)
 | 
						|
    {
 | 
						|
      size_t tmp;
 | 
						|
      hdr->column_count++;
 | 
						|
      hdr->data_size+= (tmp= dynamic_column_value_len(values + i,
 | 
						|
                        hdr->format));
 | 
						|
      if (tmp == (size_t) ~0)
 | 
						|
        return ER_DYNCOL_DATA;
 | 
						|
      hdr->nmpool_size+= (*fmt->name_size)(column_keys, i);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  /*
 | 
						|
    We can handle data up to 0x1fffffff (old format) and
 | 
						|
    0xfffffffff (new format) bytes now.
 | 
						|
  */
 | 
						|
  if ((hdr->offset_size= fmt->dynamic_column_offset_bytes(hdr->data_size)) >=
 | 
						|
      fmt->max_offset_size)
 | 
						|
    return ER_DYNCOL_LIMIT;
 | 
						|
 | 
						|
  /* header entry is column number or string pointer + offset & type */
 | 
						|
  hdr->entry_size= fmt->fixed_hdr_entry + hdr->offset_size;
 | 
						|
  hdr->header_size= hdr->column_count * hdr->entry_size;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Create packed string which contains given columns (internal multi format)
 | 
						|
 | 
						|
  @param str             String where to write the data
 | 
						|
  @param column_count    Number of columns in the arrays
 | 
						|
  @param column_keys     Array of columns keys (format dependent)
 | 
						|
  @param values          Array of columns values
 | 
						|
  @param new_str         True if we need allocate new string
 | 
						|
  @param string_keys     keys are strings
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_create_many_internal_fmt(DYNAMIC_COLUMN *str,
 | 
						|
                                        uint column_count,
 | 
						|
                                        void *column_keys,
 | 
						|
                                        DYNAMIC_COLUMN_VALUE *values,
 | 
						|
                                        my_bool new_str,
 | 
						|
                                        my_bool string_keys)
 | 
						|
{
 | 
						|
  DYN_HEADER header;
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
  bzero(&header, sizeof(header));
 | 
						|
  header.format= (string_keys ? 1 : 0);
 | 
						|
 | 
						|
  if (new_str)
 | 
						|
  {
 | 
						|
    /* to make dynstr_free() working in case of errors */
 | 
						|
    mariadb_dyncol_init(str);
 | 
						|
  }
 | 
						|
 | 
						|
  if ((rc= calc_var_sizes(&header, column_count, column_keys, values)) < 0)
 | 
						|
    return rc;
 | 
						|
 | 
						|
  return dynamic_new_column_store(str, &header,
 | 
						|
                                  column_count,
 | 
						|
                                  column_keys, values,
 | 
						|
                                  new_str);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Create packed string which contains given columns
 | 
						|
 | 
						|
  @param str             String where to write the data
 | 
						|
  @param column_count    Number of columns in the arrays
 | 
						|
  @param column_numbers  Array of columns numbers
 | 
						|
  @param values          Array of columns values
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
dynamic_column_create_many(DYNAMIC_COLUMN *str,
 | 
						|
                           uint column_count,
 | 
						|
                           uint *column_numbers,
 | 
						|
                           DYNAMIC_COLUMN_VALUE *values)
 | 
						|
{
 | 
						|
  DBUG_ENTER("dynamic_column_create_many");
 | 
						|
  DBUG_RETURN(dynamic_column_create_many_internal_fmt(str, column_count,
 | 
						|
                                                      column_numbers, values,
 | 
						|
                                                      TRUE, FALSE));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Create packed string which contains given columns
 | 
						|
 | 
						|
  @param str             String where to write the data
 | 
						|
  @param column_count    Number of columns in the arrays
 | 
						|
  @param column_numbers  Array of columns numbers
 | 
						|
  @param values          Array of columns values
 | 
						|
  @param new_string      True if we need allocate new string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_create_many_num(DYNAMIC_COLUMN *str,
 | 
						|
                               uint column_count,
 | 
						|
                               uint *column_numbers,
 | 
						|
                               DYNAMIC_COLUMN_VALUE *values,
 | 
						|
                               my_bool new_string)
 | 
						|
{
 | 
						|
  DBUG_ENTER("mariadb_dyncol_create_many_num");
 | 
						|
  DBUG_RETURN(dynamic_column_create_many_internal_fmt(str, column_count,
 | 
						|
                                                      column_numbers, values,
 | 
						|
                                                      new_string, FALSE));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Create packed string which contains given columns
 | 
						|
 | 
						|
  @param str             String where to write the data
 | 
						|
  @param column_count    Number of columns in the arrays
 | 
						|
  @param column_keys     Array of columns keys
 | 
						|
  @param values          Array of columns value
 | 
						|
  @param new_string      True if we need allocate new string
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_create_many_named(DYNAMIC_COLUMN *str,
 | 
						|
                                 uint column_count,
 | 
						|
                                 LEX_STRING *column_keys,
 | 
						|
                                 DYNAMIC_COLUMN_VALUE *values,
 | 
						|
                                 my_bool new_string)
 | 
						|
{
 | 
						|
  DBUG_ENTER("mariadb_dyncol_create_many_named");
 | 
						|
  DBUG_RETURN(dynamic_column_create_many_internal_fmt(str, column_count,
 | 
						|
                                                      column_keys, values,
 | 
						|
                                                      new_string, TRUE));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Create packed string which contains given column
 | 
						|
 | 
						|
  @param str             String where to write the data
 | 
						|
  @param column_number   Column number
 | 
						|
  @param value           The columns value
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
dynamic_column_create(DYNAMIC_COLUMN *str, uint column_nr,
 | 
						|
                      DYNAMIC_COLUMN_VALUE *value)
 | 
						|
{
 | 
						|
  DBUG_ENTER("dynamic_column_create");
 | 
						|
  DBUG_RETURN(dynamic_column_create_many(str, 1, &column_nr, value));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate length of data between given two header entries
 | 
						|
 | 
						|
  @param entry           Pointer to the first entry
 | 
						|
  @param entry_next      Pointer to the last entry
 | 
						|
  @param header_end      Pointer to the header end
 | 
						|
  @param offset_size     Size of offset field in bytes
 | 
						|
  @param last_offset     Size of the data segment
 | 
						|
 | 
						|
  @return number of bytes
 | 
						|
*/
 | 
						|
 | 
						|
static size_t get_length_interval(uchar *entry, uchar *entry_next,
 | 
						|
                                  uchar *header_end, size_t offset_size,
 | 
						|
                                  size_t last_offset)
 | 
						|
{
 | 
						|
  size_t offset, offset_next;
 | 
						|
  DYNAMIC_COLUMN_TYPE type, type_next;
 | 
						|
  DBUG_ASSERT(entry < entry_next);
 | 
						|
 | 
						|
  if (type_and_offset_read_num(&type, &offset, entry + COLUMN_NUMBER_SIZE,
 | 
						|
                               offset_size))
 | 
						|
      return DYNCOL_OFFSET_ERROR;
 | 
						|
  if (entry_next >= header_end)
 | 
						|
    return (last_offset - offset);
 | 
						|
  if (type_and_offset_read_num(&type_next, &offset_next,
 | 
						|
                               entry_next + COLUMN_NUMBER_SIZE, offset_size) ||
 | 
						|
      (offset_next > last_offset))
 | 
						|
    return DYNCOL_OFFSET_ERROR;
 | 
						|
  return (offset_next - offset);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate length of data between given hdr->entry and next_entry
 | 
						|
 | 
						|
  @param hdr             descriptor of dynamic column record
 | 
						|
  @param next_entry      next header entry (can point just after last header
 | 
						|
                         entry)
 | 
						|
 | 
						|
  @return number of bytes
 | 
						|
*/
 | 
						|
 | 
						|
static size_t hdr_interval_length(DYN_HEADER *hdr, uchar *next_entry)
 | 
						|
{
 | 
						|
  struct st_service_funcs *fmt= fmt_data + hdr->format;
 | 
						|
  size_t next_entry_offset;
 | 
						|
  DYNAMIC_COLUMN_TYPE next_entry_type;
 | 
						|
  DBUG_ASSERT(hdr->entry < next_entry);
 | 
						|
  DBUG_ASSERT(hdr->entry >= hdr->header);
 | 
						|
  DBUG_ASSERT(next_entry <= hdr->header + hdr->header_size);
 | 
						|
 | 
						|
  if ((*fmt->type_and_offset_read)(&hdr->type, &hdr->offset,
 | 
						|
                                   hdr->entry + fmt->fixed_hdr_entry,
 | 
						|
                                   hdr->offset_size) ||
 | 
						|
      hdr->data_size < hdr->offset)
 | 
						|
    return DYNCOL_OFFSET_ERROR;
 | 
						|
  if (next_entry == hdr->header + hdr->header_size)
 | 
						|
    return hdr->data_size - hdr->offset;
 | 
						|
  if ((*fmt->type_and_offset_read)(&next_entry_type, &next_entry_offset,
 | 
						|
                                   next_entry + fmt->fixed_hdr_entry,
 | 
						|
                                   hdr->offset_size) ||
 | 
						|
      hdr->data_size < next_entry_offset)
 | 
						|
    return DYNCOL_OFFSET_ERROR;
 | 
						|
  return (next_entry_offset - hdr->offset);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Comparator function for references to header entries for qsort
 | 
						|
*/
 | 
						|
 | 
						|
static int header_compar_num(const void *a, const void *b)
 | 
						|
{
 | 
						|
  uint va= uint2korr((uchar*)a), vb= uint2korr((uchar*)b);
 | 
						|
  return (va > vb ? 1 : (va < vb ? -1 : 0));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Find entry in the numeric format header by the column number
 | 
						|
 | 
						|
  @param hdr             descriptor of dynamic column record
 | 
						|
  @param key             number to find
 | 
						|
 | 
						|
  @return pointer to the entry or NULL
 | 
						|
*/
 | 
						|
 | 
						|
static uchar *find_entry_num(DYN_HEADER *hdr, uint key)
 | 
						|
{
 | 
						|
  uchar header_entry[2+4];
 | 
						|
  DBUG_ASSERT(hdr->format == dyncol_fmt_num);
 | 
						|
  int2store(header_entry, key);
 | 
						|
  return hdr->entry= bsearch(header_entry, hdr->header,
 | 
						|
                             (size_t)hdr->column_count,
 | 
						|
                             hdr->entry_size, &header_compar_num);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Read name from header entry
 | 
						|
 | 
						|
  @param hdr             descriptor of dynamic column record
 | 
						|
  @param entry           pointer to the header entry
 | 
						|
  @param name            where to put name
 | 
						|
 | 
						|
  @return 0 ok
 | 
						|
  @return 1 error in data
 | 
						|
*/
 | 
						|
 | 
						|
static my_bool read_name(DYN_HEADER *hdr, uchar *entry, LEX_STRING *name)
 | 
						|
{
 | 
						|
  size_t nmoffset= uint2korr(entry);
 | 
						|
  uchar *next_entry= entry + hdr->entry_size;
 | 
						|
 | 
						|
  if (nmoffset > hdr->nmpool_size)
 | 
						|
    return 1;
 | 
						|
 | 
						|
  name->str= (char *)hdr->nmpool + nmoffset;
 | 
						|
  if (next_entry == hdr->header + hdr->header_size)
 | 
						|
    name->length= hdr->nmpool_size - nmoffset;
 | 
						|
  else
 | 
						|
  {
 | 
						|
    size_t next_nmoffset= uint2korr(next_entry);
 | 
						|
    if (next_nmoffset > hdr->nmpool_size)
 | 
						|
      return 1;
 | 
						|
    name->length= next_nmoffset - nmoffset;
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Find entry in the names format header by the column number
 | 
						|
 | 
						|
  @param hdr             descriptor of dynamic column record
 | 
						|
  @param key             name to find
 | 
						|
 | 
						|
  @return pointer to the entry or NULL
 | 
						|
*/
 | 
						|
static uchar *find_entry_named(DYN_HEADER *hdr, LEX_STRING *key)
 | 
						|
{
 | 
						|
  uchar *min= hdr->header;
 | 
						|
  uchar *max= hdr->header + (hdr->column_count - 1) * hdr->entry_size;
 | 
						|
  uchar *mid;
 | 
						|
  DBUG_ASSERT(hdr->format == dyncol_fmt_str);
 | 
						|
  DBUG_ASSERT(hdr->nmpool != NULL);
 | 
						|
  while (max >= min)
 | 
						|
  {
 | 
						|
    LEX_STRING name;
 | 
						|
    int cmp;
 | 
						|
    mid= hdr->header + ((min - hdr->header) +
 | 
						|
                        (max - hdr->header)) /
 | 
						|
      2 /
 | 
						|
      hdr->entry_size * hdr->entry_size;
 | 
						|
    if (read_name(hdr, mid, &name))
 | 
						|
      return NULL;
 | 
						|
    cmp= mariadb_dyncol_column_cmp_named(&name, key);
 | 
						|
    if (cmp < 0)
 | 
						|
      min= mid + hdr->entry_size;
 | 
						|
    else if (cmp > 0)
 | 
						|
      max= mid - hdr->entry_size;
 | 
						|
    else
 | 
						|
      return mid;
 | 
						|
  }
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Write number in the buffer (backward direction - starts from the buffer end)
 | 
						|
 | 
						|
  @return pointer on the number beginning
 | 
						|
*/
 | 
						|
 | 
						|
static char *backwritenum(char *chr, uint numkey)
 | 
						|
{
 | 
						|
  if (numkey == 0)
 | 
						|
    *(--chr)= '0';
 | 
						|
  else
 | 
						|
    while (numkey > 0)
 | 
						|
    {
 | 
						|
      *(--chr)= '0' + numkey % 10;
 | 
						|
      numkey/= 10;
 | 
						|
    }
 | 
						|
  return chr;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Find column and fill information about it
 | 
						|
 | 
						|
  @param hdr             descriptor of dynamic column record
 | 
						|
  @param numkey          Number of the column to fetch (if strkey is NULL)
 | 
						|
  @param strkey          Name of the column to fetch (or NULL)
 | 
						|
 | 
						|
  @return 0 ok
 | 
						|
  @return 1 error in data
 | 
						|
*/
 | 
						|
 | 
						|
static my_bool
 | 
						|
find_column(DYN_HEADER *hdr, uint numkey, LEX_STRING *strkey)
 | 
						|
{
 | 
						|
  LEX_STRING nmkey;
 | 
						|
  char nmkeybuff[DYNCOL_NUM_CHAR]; /* to fit max 2 bytes number */
 | 
						|
  DBUG_ASSERT(hdr->header != NULL);
 | 
						|
 | 
						|
  if (hdr->header + hdr->header_size > hdr->data_end)
 | 
						|
    return TRUE;
 | 
						|
 | 
						|
  /* fix key */
 | 
						|
  if (hdr->format == dyncol_fmt_num && strkey != NULL)
 | 
						|
  {
 | 
						|
    char *end;
 | 
						|
    numkey= (uint) strtoul(strkey->str, &end, 10);
 | 
						|
    if (end != strkey->str + strkey->length)
 | 
						|
    {
 | 
						|
      /* we can't find non-numeric key among numeric ones */
 | 
						|
      hdr->type= DYN_COL_NULL;
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (hdr->format == dyncol_fmt_str && strkey == NULL)
 | 
						|
  {
 | 
						|
    nmkey.str= backwritenum(nmkeybuff + sizeof(nmkeybuff), numkey);
 | 
						|
    nmkey.length= (nmkeybuff + sizeof(nmkeybuff)) - nmkey.str;
 | 
						|
    strkey= &nmkey;
 | 
						|
  }
 | 
						|
  if (hdr->format == dyncol_fmt_num)
 | 
						|
    hdr->entry= find_entry_num(hdr, numkey);
 | 
						|
  else
 | 
						|
    hdr->entry= find_entry_named(hdr, strkey);
 | 
						|
 | 
						|
  if (!hdr->entry)
 | 
						|
  {
 | 
						|
    /* Column not found */
 | 
						|
    hdr->type= DYN_COL_NULL;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  hdr->length= hdr_interval_length(hdr, hdr->entry + hdr->entry_size);
 | 
						|
  hdr->data= hdr->dtpool + hdr->offset;
 | 
						|
  /*
 | 
						|
    Check that the found data is within the ranges. This can happen if
 | 
						|
    we get data with wrong offsets.
 | 
						|
  */
 | 
						|
  if (hdr->length == DYNCOL_OFFSET_ERROR ||
 | 
						|
      hdr->length > INT_MAX || hdr->offset > hdr->data_size)
 | 
						|
    return 1;
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Read and check the header of the dynamic string
 | 
						|
 | 
						|
  @param hdr             descriptor of dynamic column record
 | 
						|
  @param str             Dynamic string
 | 
						|
 | 
						|
  @retval FALSE OK
 | 
						|
  @retval TRUE  error
 | 
						|
 | 
						|
  Note
 | 
						|
    We don't check for str->length == 0 as all code that calls this
 | 
						|
    already have handled this case.
 | 
						|
*/
 | 
						|
 | 
						|
static inline my_bool read_fixed_header(DYN_HEADER *hdr,
 | 
						|
                                        DYNAMIC_COLUMN *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(str != NULL && str->length != 0);
 | 
						|
  if ((str->length < 1)  ||
 | 
						|
      (str->str[0] & (~DYNCOL_FLG_KNOWN)))
 | 
						|
    return 1;
 | 
						|
  hdr->format= ((str->str[0] & DYNCOL_FLG_NAMES) ?
 | 
						|
                dyncol_fmt_str:
 | 
						|
                dyncol_fmt_num);
 | 
						|
  if ((str->length < fmt_data[hdr->format].fixed_hdr))
 | 
						|
    return 1;                                   /* Wrong header */
 | 
						|
  hdr->offset_size= (str->str[0] & DYNCOL_FLG_OFFSET) + 1 +
 | 
						|
    (hdr->format == dyncol_fmt_str ? 1 : 0);
 | 
						|
  hdr->column_count= uint2korr(str->str + 1);
 | 
						|
  if (hdr->format == dyncol_fmt_str)
 | 
						|
    hdr->nmpool_size= uint2korr(str->str + 3); // only 2 bytes supported for now
 | 
						|
  else
 | 
						|
    hdr->nmpool_size= 0;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Get dynamic column value by column number
 | 
						|
 | 
						|
  @param str             The packed string to extract the column
 | 
						|
  @param column_nr       Number of column to fetch
 | 
						|
  @param store_it_here   Where to store the extracted value
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
dynamic_column_get(DYNAMIC_COLUMN *str, uint column_nr,
 | 
						|
                       DYNAMIC_COLUMN_VALUE *store_it_here)
 | 
						|
{
 | 
						|
  return dynamic_column_get_internal(str, store_it_here, column_nr, NULL);
 | 
						|
}
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_get_num(DYNAMIC_COLUMN *str, uint column_nr,
 | 
						|
                       DYNAMIC_COLUMN_VALUE *store_it_here)
 | 
						|
{
 | 
						|
  return dynamic_column_get_internal(str, store_it_here, column_nr, NULL);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Get dynamic column value by name
 | 
						|
 | 
						|
  @param str             The packed string to extract the column
 | 
						|
  @param name            Name of column to fetch
 | 
						|
  @param store_it_here   Where to store the extracted value
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_get_named(DYNAMIC_COLUMN *str, LEX_STRING *name,
 | 
						|
                         DYNAMIC_COLUMN_VALUE *store_it_here)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(name != NULL);
 | 
						|
  return dynamic_column_get_internal(str, store_it_here, 0, name);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_get_value(DYN_HEADER *hdr, DYNAMIC_COLUMN_VALUE *store_it_here)
 | 
						|
{
 | 
						|
  static enum enum_dyncol_func_result rc;
 | 
						|
  switch ((store_it_here->type= hdr->type)) {
 | 
						|
  case DYN_COL_INT:
 | 
						|
    rc= dynamic_column_sint_read(store_it_here, hdr->data, hdr->length);
 | 
						|
    break;
 | 
						|
  case DYN_COL_UINT:
 | 
						|
    rc= dynamic_column_uint_read(store_it_here, hdr->data, hdr->length);
 | 
						|
    break;
 | 
						|
  case DYN_COL_DOUBLE:
 | 
						|
    rc= dynamic_column_double_read(store_it_here, hdr->data, hdr->length);
 | 
						|
    break;
 | 
						|
  case DYN_COL_STRING:
 | 
						|
    rc= dynamic_column_string_read(store_it_here, hdr->data, hdr->length);
 | 
						|
    break;
 | 
						|
  case DYN_COL_DECIMAL:
 | 
						|
    rc= dynamic_column_decimal_read(store_it_here, hdr->data, hdr->length);
 | 
						|
    break;
 | 
						|
  case DYN_COL_DATETIME:
 | 
						|
    rc= dynamic_column_date_time_read(store_it_here, hdr->data,
 | 
						|
                                      hdr->length);
 | 
						|
    break;
 | 
						|
  case DYN_COL_DATE:
 | 
						|
    rc= dynamic_column_date_read(store_it_here, hdr->data, hdr->length);
 | 
						|
    break;
 | 
						|
  case DYN_COL_TIME:
 | 
						|
    rc= dynamic_column_time_read(store_it_here, hdr->data, hdr->length);
 | 
						|
    break;
 | 
						|
  case DYN_COL_NULL:
 | 
						|
    rc= ER_DYNCOL_OK;
 | 
						|
    break;
 | 
						|
  case DYN_COL_DYNCOL:
 | 
						|
    rc= dynamic_column_dyncol_read(store_it_here, hdr->data, hdr->length);
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    rc= ER_DYNCOL_FORMAT;
 | 
						|
    store_it_here->type= DYN_COL_NULL;
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Get dynamic column value by number or name
 | 
						|
 | 
						|
  @param str             The packed string to extract the column
 | 
						|
  @param store_it_here   Where to store the extracted value
 | 
						|
  @param numkey          Number of the column to fetch (if strkey is NULL)
 | 
						|
  @param strkey          Name of the column to fetch (or NULL)
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_get_internal(DYNAMIC_COLUMN *str,
 | 
						|
                            DYNAMIC_COLUMN_VALUE *store_it_here,
 | 
						|
                            uint num_key, LEX_STRING *str_key)
 | 
						|
{
 | 
						|
  DYN_HEADER header;
 | 
						|
  enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT;
 | 
						|
  bzero(&header, sizeof(header));
 | 
						|
 | 
						|
  if (str->length == 0)
 | 
						|
    goto null;
 | 
						|
 | 
						|
  if ((rc= init_read_hdr(&header, str)) < 0)
 | 
						|
    goto err;
 | 
						|
 | 
						|
  if (header.column_count == 0)
 | 
						|
    goto null;
 | 
						|
 | 
						|
  if (find_column(&header, num_key, str_key))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  rc= dynamic_column_get_value(&header, store_it_here);
 | 
						|
  return rc;
 | 
						|
 | 
						|
null:
 | 
						|
    rc= ER_DYNCOL_OK;
 | 
						|
err:
 | 
						|
    store_it_here->type= DYN_COL_NULL;
 | 
						|
    return rc;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Check existence of the column in the packed string (by number)
 | 
						|
 | 
						|
  @param str             The packed string to check the column
 | 
						|
  @param column_nr       Number of column to check
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
dynamic_column_exists(DYNAMIC_COLUMN *str, uint column_nr)
 | 
						|
{
 | 
						|
  return dynamic_column_exists_internal(str, column_nr, NULL);
 | 
						|
}
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_exists_num(DYNAMIC_COLUMN *str, uint column_nr)
 | 
						|
{
 | 
						|
  return dynamic_column_exists_internal(str, column_nr, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Check existence of the column in the packed string (by name)
 | 
						|
 | 
						|
  @param str             The packed string to check the column
 | 
						|
  @param name            Name of column to check
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_exists_named(DYNAMIC_COLUMN *str, LEX_STRING *name)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(name != NULL);
 | 
						|
  return dynamic_column_exists_internal(str, 0, name);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Check existence of the column in the packed string (by name of number)
 | 
						|
 | 
						|
  @param str             The packed string to check the column
 | 
						|
  @param num_key         Number of the column to fetch (if strkey is NULL)
 | 
						|
  @param str_key         Name of the column to fetch (or NULL)
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_exists_internal(DYNAMIC_COLUMN *str, uint num_key,
 | 
						|
                               LEX_STRING *str_key)
 | 
						|
{
 | 
						|
  DYN_HEADER header;
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
  bzero(&header, sizeof(header));
 | 
						|
 | 
						|
  if (str->length == 0)
 | 
						|
    return ER_DYNCOL_NO;                        /* no columns */
 | 
						|
 | 
						|
  if ((rc= init_read_hdr(&header, str)) < 0)
 | 
						|
    return rc;
 | 
						|
 | 
						|
  if (header.column_count == 0)
 | 
						|
    return ER_DYNCOL_NO;                        /* no columns */
 | 
						|
 | 
						|
  if (find_column(&header, num_key, str_key))
 | 
						|
    return ER_DYNCOL_FORMAT;
 | 
						|
 | 
						|
  return (header.type != DYN_COL_NULL ? ER_DYNCOL_YES : ER_DYNCOL_NO);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  List not-null columns in the packed string (only numeric format)
 | 
						|
 | 
						|
  @param str             The packed string
 | 
						|
  @param array_of_uint   Where to put reference on created array
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
enum enum_dyncol_func_result
 | 
						|
dynamic_column_list(DYNAMIC_COLUMN *str, DYNAMIC_ARRAY *array_of_uint)
 | 
						|
{
 | 
						|
  DYN_HEADER header;
 | 
						|
  uchar *read;
 | 
						|
  uint i;
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
 | 
						|
  bzero(array_of_uint, sizeof(*array_of_uint)); /* In case of errors */
 | 
						|
  if (str->length == 0)
 | 
						|
    return ER_DYNCOL_OK;                        /* no columns */
 | 
						|
 | 
						|
  if ((rc= init_read_hdr(&header, str)) < 0)
 | 
						|
    return rc;
 | 
						|
 | 
						|
  if (header.format != dyncol_fmt_num)
 | 
						|
    return ER_DYNCOL_FORMAT;
 | 
						|
 | 
						|
  if (header.entry_size * header.column_count + FIXED_HEADER_SIZE >
 | 
						|
      str->length)
 | 
						|
    return ER_DYNCOL_FORMAT;
 | 
						|
 | 
						|
  if (my_init_dynamic_array(PSI_INSTRUMENT_ME, array_of_uint,
 | 
						|
                            sizeof(uint), header.column_count, 0, MYF(0)))
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
 | 
						|
  for (i= 0, read= header.header;
 | 
						|
       i < header.column_count;
 | 
						|
       i++, read+= header.entry_size)
 | 
						|
  {
 | 
						|
    uint nm= uint2korr(read);
 | 
						|
    /* Insert can't never fail as it's pre-allocated above */
 | 
						|
    (void) insert_dynamic(array_of_uint, (uchar *)&nm);
 | 
						|
  }
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  List not-null columns in the packed string (only numeric format)
 | 
						|
 | 
						|
  @param str             The packed string
 | 
						|
  @param array_of_uint   Where to put reference on created array
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_list_num(DYNAMIC_COLUMN *str, uint *count, uint **nums)
 | 
						|
{
 | 
						|
  DYN_HEADER header;
 | 
						|
  uchar *read;
 | 
						|
  uint i;
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
 | 
						|
  (*nums)= 0; (*count)= 0;                      /* In case of errors */
 | 
						|
 | 
						|
  if (str->length == 0)
 | 
						|
    return ER_DYNCOL_OK;                        /* no columns */
 | 
						|
 | 
						|
  if ((rc= init_read_hdr(&header, str)) < 0)
 | 
						|
    return rc;
 | 
						|
 | 
						|
  if (header.format != dyncol_fmt_num)
 | 
						|
    return ER_DYNCOL_FORMAT;
 | 
						|
 | 
						|
  if (header.entry_size * header.column_count + FIXED_HEADER_SIZE >
 | 
						|
      str->length)
 | 
						|
    return ER_DYNCOL_FORMAT;
 | 
						|
 | 
						|
  if (!((*nums)= my_malloc(PSI_INSTRUMENT_ME, sizeof(uint) * header.column_count, MYF(0))))
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
 | 
						|
  for (i= 0, read= header.header;
 | 
						|
       i < header.column_count;
 | 
						|
       i++, read+= header.entry_size)
 | 
						|
  {
 | 
						|
    (*nums)[i]= uint2korr(read);
 | 
						|
  }
 | 
						|
  (*count)= header.column_count;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  List not-null columns in the packed string (any format)
 | 
						|
 | 
						|
  @param str             The packed string
 | 
						|
  @param count           Number of names in the list
 | 
						|
  @param names           Where to put names list (should be freed)
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_list_named(DYNAMIC_COLUMN *str, uint *count, LEX_STRING **names)
 | 
						|
{
 | 
						|
  DYN_HEADER header;
 | 
						|
  uchar *read;
 | 
						|
  char *pool;
 | 
						|
  struct st_service_funcs *fmt;
 | 
						|
  uint i;
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
 | 
						|
  (*names)= 0; (*count)= 0;
 | 
						|
 | 
						|
  if (str->length == 0)
 | 
						|
    return ER_DYNCOL_OK;                        /* no columns */
 | 
						|
 | 
						|
  if ((rc= init_read_hdr(&header, str)) < 0)
 | 
						|
    return rc;
 | 
						|
 | 
						|
  fmt= fmt_data + header.format;
 | 
						|
 | 
						|
  if (header.entry_size * header.column_count + fmt->fixed_hdr >
 | 
						|
      str->length)
 | 
						|
    return ER_DYNCOL_FORMAT;
 | 
						|
 | 
						|
  {
 | 
						|
    size_t size;
 | 
						|
    if (header.format == dyncol_fmt_num)
 | 
						|
      size= DYNCOL_NUM_CHAR * header.column_count;
 | 
						|
    else
 | 
						|
      size= header.nmpool_size + header.column_count;
 | 
						|
 | 
						|
    *names= my_malloc(PSI_INSTRUMENT_ME,
 | 
						|
                      sizeof(LEX_STRING) * header.column_count + size, MYF(0));
 | 
						|
  }
 | 
						|
 | 
						|
  if (!(*names))
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
  pool= ((char *)(*names)) + sizeof(LEX_STRING) * header.column_count;
 | 
						|
 | 
						|
  for (i= 0, read= header.header;
 | 
						|
       i < header.column_count;
 | 
						|
       i++, read+= header.entry_size)
 | 
						|
  {
 | 
						|
    if (header.format == dyncol_fmt_num)
 | 
						|
    {
 | 
						|
      uint nm= uint2korr(read);
 | 
						|
      (*names)[i].str= pool;
 | 
						|
      pool+= DYNCOL_NUM_CHAR;
 | 
						|
      (*names)[i].length=
 | 
						|
        longlong2str(nm, (*names)[i].str, 10) - (*names)[i].str;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      LEX_STRING tmp;
 | 
						|
      if (read_name(&header, read, &tmp))
 | 
						|
        return ER_DYNCOL_FORMAT;
 | 
						|
      (*names)[i].length= tmp.length;
 | 
						|
      (*names)[i].str= pool;
 | 
						|
      pool+= tmp.length + 1;
 | 
						|
      memcpy((*names)[i].str, (const void *)tmp.str, tmp.length);
 | 
						|
      (*names)[i].str[tmp.length]= '\0'; // just for safety
 | 
						|
    }
 | 
						|
  }
 | 
						|
  (*count)= header.column_count;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Find the place of the column in the header or place where it should be put
 | 
						|
 | 
						|
  @param hdr             descriptor of dynamic column record
 | 
						|
  @param key             Name or number of column to fetch
 | 
						|
                         (depends on string_key)
 | 
						|
  @param string_key      True if we gave pointer to LEX_STRING.
 | 
						|
 | 
						|
  @retval TRUE found
 | 
						|
  @retval FALSE pointer set to the next row
 | 
						|
*/
 | 
						|
 | 
						|
static my_bool
 | 
						|
find_place(DYN_HEADER *hdr, void *key, my_bool string_keys)
 | 
						|
{
 | 
						|
  uint mid, start, end, val;
 | 
						|
  int UNINIT_VAR(flag);
 | 
						|
  LEX_STRING str;
 | 
						|
  char buff[DYNCOL_NUM_CHAR];
 | 
						|
  my_bool need_conversion= ((string_keys ? dyncol_fmt_str : dyncol_fmt_num) !=
 | 
						|
                            hdr->format);
 | 
						|
  /* new format can't be numeric if the old one is names */
 | 
						|
  DBUG_ASSERT(string_keys ||
 | 
						|
              hdr->format == dyncol_fmt_num);
 | 
						|
 | 
						|
  start= 0;
 | 
						|
  end= hdr->column_count -1;
 | 
						|
  mid= 1;
 | 
						|
  while (start != end)
 | 
						|
  {
 | 
						|
    mid= (start + end) / 2;
 | 
						|
    hdr->entry= hdr->header + mid * hdr->entry_size;
 | 
						|
    if (!string_keys)
 | 
						|
    {
 | 
						|
      val= uint2korr(hdr->entry);
 | 
						|
      flag= CMP_NUM(*((uint *)key), val);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if (need_conversion)
 | 
						|
      {
 | 
						|
        str.str= backwritenum(buff + sizeof(buff), uint2korr(hdr->entry));
 | 
						|
        str.length= (buff + sizeof(buff)) - str.str;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        DBUG_ASSERT(hdr->format == dyncol_fmt_str);
 | 
						|
        if (read_name(hdr, hdr->entry, &str))
 | 
						|
          return 0;
 | 
						|
      }
 | 
						|
      flag= mariadb_dyncol_column_cmp_named((LEX_STRING *)key, &str);
 | 
						|
    }
 | 
						|
    if (flag <= 0)
 | 
						|
      end= mid;
 | 
						|
    else
 | 
						|
      start= mid + 1;
 | 
						|
  }
 | 
						|
  hdr->entry= hdr->header + start * hdr->entry_size;
 | 
						|
  if (start != mid)
 | 
						|
  {
 | 
						|
    if (!string_keys)
 | 
						|
    {
 | 
						|
      val= uint2korr(hdr->entry);
 | 
						|
      flag= CMP_NUM(*((uint *)key), val);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if (need_conversion)
 | 
						|
      {
 | 
						|
        str.str= backwritenum(buff + sizeof(buff), uint2korr(hdr->entry));
 | 
						|
        str.length= (buff + sizeof(buff)) - str.str;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        DBUG_ASSERT(hdr->format == dyncol_fmt_str);
 | 
						|
        if (read_name(hdr, hdr->entry, &str))
 | 
						|
          return 0;
 | 
						|
      }
 | 
						|
      flag= mariadb_dyncol_column_cmp_named((LEX_STRING *)key, &str);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (flag > 0)
 | 
						|
    hdr->entry+= hdr->entry_size; /* Point at next bigger key */
 | 
						|
  return flag == 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  It is internal structure which describes a plan of changing the record
 | 
						|
  of dynamic columns
 | 
						|
*/
 | 
						|
 | 
						|
typedef enum {PLAN_REPLACE, PLAN_ADD, PLAN_DELETE, PLAN_NOP} PLAN_ACT;
 | 
						|
 | 
						|
struct st_plan {
 | 
						|
  DYNAMIC_COLUMN_VALUE *val;
 | 
						|
  void *key;
 | 
						|
  uchar *place;
 | 
						|
  size_t length;
 | 
						|
  long long hdelta, ddelta, ndelta;
 | 
						|
  long long mv_offset, mv_length;
 | 
						|
  uint mv_end;
 | 
						|
  PLAN_ACT act;
 | 
						|
};
 | 
						|
typedef struct st_plan PLAN;
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Sort function for plan by column number
 | 
						|
*/
 | 
						|
 | 
						|
static int plan_sort_num(const void *a, const void *b)
 | 
						|
{
 | 
						|
  return *((uint *)((PLAN *)a)->key) - *((uint *)((PLAN *)b)->key);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Sort function for plan by column name
 | 
						|
*/
 | 
						|
 | 
						|
static int plan_sort_named(const void *a, const void *b)
 | 
						|
{
 | 
						|
  return mariadb_dyncol_column_cmp_named((LEX_STRING *)((PLAN *)a)->key,
 | 
						|
                                         (LEX_STRING *)((PLAN *)b)->key);
 | 
						|
}
 | 
						|
 | 
						|
#define DELTA_CHECK(S, D, C)        \
 | 
						|
  if ((S) == 0)                     \
 | 
						|
    (S)= (D);                       \
 | 
						|
  else if (((S) > 0 && (D) < 0) ||  \
 | 
						|
            ((S) < 0 && (D) > 0))   \
 | 
						|
  {                                 \
 | 
						|
    (C)= TRUE;                      \
 | 
						|
  }
 | 
						|
 | 
						|
/**
 | 
						|
  Update dynamic column by copying in a new record (string).
 | 
						|
 | 
						|
  @param str             Dynamic column record to change
 | 
						|
  @param plan            Plan of changing the record
 | 
						|
  @param add_column_count number of records in the plan array.
 | 
						|
  @param hdr             descriptor of old dynamic column record
 | 
						|
  @param new_hdr         descriptor of new dynamic column record
 | 
						|
  @param convert         need conversion from numeric to names format
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_update_copy(DYNAMIC_COLUMN *str, PLAN *plan,
 | 
						|
                           uint add_column_count,
 | 
						|
                           DYN_HEADER *hdr, DYN_HEADER *new_hdr,
 | 
						|
                           my_bool convert)
 | 
						|
{
 | 
						|
  DYNAMIC_COLUMN tmp;
 | 
						|
  struct st_service_funcs *fmt= fmt_data + hdr->format,
 | 
						|
                          *new_fmt= fmt_data + new_hdr->format;
 | 
						|
  uint i, j, k;
 | 
						|
  size_t all_headers_size;
 | 
						|
 | 
						|
  if (dynamic_column_init_named(&tmp,
 | 
						|
                              (new_fmt->fixed_hdr + new_hdr->header_size +
 | 
						|
                               new_hdr->nmpool_size +
 | 
						|
                               new_hdr->data_size + DYNCOL_SYZERESERVE)))
 | 
						|
  {
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
  }
 | 
						|
  bzero(tmp.str, new_fmt->fixed_hdr);
 | 
						|
  (*new_fmt->set_fixed_hdr)(&tmp, new_hdr);
 | 
						|
  /* Adjust tmp to contain whole the future header */
 | 
						|
  tmp.length= new_fmt->fixed_hdr + new_hdr->header_size + new_hdr->nmpool_size;
 | 
						|
 | 
						|
 | 
						|
  /*
 | 
						|
    Copy data to the new string
 | 
						|
    i= index in array of changes
 | 
						|
    j= index in packed string header index
 | 
						|
  */
 | 
						|
  new_hdr->entry= new_hdr->header;
 | 
						|
  new_hdr->name= new_hdr->nmpool;
 | 
						|
  all_headers_size= new_fmt->fixed_hdr +
 | 
						|
    new_hdr->header_size + new_hdr->nmpool_size;
 | 
						|
  for (i= 0, j= 0; i < add_column_count || j < hdr->column_count; i++)
 | 
						|
  {
 | 
						|
    size_t UNINIT_VAR(first_offset);
 | 
						|
    uint start= j, end;
 | 
						|
 | 
						|
    /*
 | 
						|
      Search in i and j for the next column to add from i and where to
 | 
						|
      add.
 | 
						|
    */
 | 
						|
 | 
						|
    while (i < add_column_count && plan[i].act == PLAN_NOP)
 | 
						|
      i++;                                    /* skip NOP */
 | 
						|
 | 
						|
    if (i == add_column_count)
 | 
						|
      j= end= hdr->column_count;
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        old data portion. We don't need to check that j < column_count
 | 
						|
        as plan[i].place is guaranteed to have a pointer inside the
 | 
						|
        data.
 | 
						|
      */
 | 
						|
      while (hdr->header + j * hdr->entry_size < plan[i].place)
 | 
						|
        j++;
 | 
						|
      end= j;
 | 
						|
      if ((plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
 | 
						|
        j++;                              /* data at 'j' will be removed */
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
      Adjust all headers since last loop.
 | 
						|
      We have to do this as the offset for data has moved
 | 
						|
    */
 | 
						|
    for (k= start; k < end; k++)
 | 
						|
    {
 | 
						|
      uchar *read= hdr->header + k * hdr->entry_size;
 | 
						|
      void *key;
 | 
						|
      LEX_STRING name;
 | 
						|
      size_t offs;
 | 
						|
      uint nm;
 | 
						|
      DYNAMIC_COLUMN_TYPE tp;
 | 
						|
      char buff[DYNCOL_NUM_CHAR];
 | 
						|
 | 
						|
      if (hdr->format == dyncol_fmt_num)
 | 
						|
      {
 | 
						|
        if (convert)
 | 
						|
        {
 | 
						|
          name.str= backwritenum(buff + sizeof(buff), uint2korr(read));
 | 
						|
          name.length= (buff + sizeof(buff)) - name.str;
 | 
						|
          key= &name;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          nm= uint2korr(read);                    /* Column nummber */
 | 
						|
          key= &nm;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        if (read_name(hdr, read, &name))
 | 
						|
          goto err;
 | 
						|
        key= &name;
 | 
						|
      }
 | 
						|
      if (fmt->type_and_offset_read(&tp, &offs,
 | 
						|
                                    read + fmt->fixed_hdr_entry,
 | 
						|
                                    hdr->offset_size))
 | 
						|
          goto err;
 | 
						|
      if (k == start)
 | 
						|
        first_offset= offs;
 | 
						|
      else if (offs < first_offset)
 | 
						|
        goto err;
 | 
						|
 | 
						|
      offs+= (size_t) plan[i].ddelta;
 | 
						|
      {
 | 
						|
        DYNAMIC_COLUMN_VALUE val;
 | 
						|
        val.type= tp; // only the type used in the header
 | 
						|
        if ((*new_fmt->put_header_entry)(new_hdr, key, &val, offs))
 | 
						|
          goto err;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /* copy first the data that was not replaced in original packed data */
 | 
						|
    if (start < end)
 | 
						|
    {
 | 
						|
      size_t data_size;
 | 
						|
      /* Add old data last in 'tmp' */
 | 
						|
      hdr->entry= hdr->header + start * hdr->entry_size;
 | 
						|
      data_size=
 | 
						|
        hdr_interval_length(hdr, hdr->header + end * hdr->entry_size);
 | 
						|
      if (data_size == DYNCOL_OFFSET_ERROR ||
 | 
						|
          (long) data_size < 0 ||
 | 
						|
          data_size > hdr->data_size - first_offset)
 | 
						|
        goto err;
 | 
						|
 | 
						|
      memcpy(tmp.str + tmp.length, (char *)hdr->dtpool + first_offset,
 | 
						|
             data_size);
 | 
						|
      tmp.length+= data_size;
 | 
						|
    }
 | 
						|
 | 
						|
    /* new data adding */
 | 
						|
    if (i < add_column_count)
 | 
						|
    {
 | 
						|
      if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
 | 
						|
      {
 | 
						|
        if ((*new_fmt->put_header_entry)(new_hdr, plan[i].key,
 | 
						|
                                         plan[i].val,
 | 
						|
                                         tmp.length - all_headers_size))
 | 
						|
          goto err;
 | 
						|
        data_store(&tmp, plan[i].val, new_hdr->format); /* Append new data */
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  mariadb_dyncol_free(str);
 | 
						|
  *str= tmp;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
err:
 | 
						|
  mariadb_dyncol_free(&tmp);
 | 
						|
  return ER_DYNCOL_FORMAT;
 | 
						|
}
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_update_move_left(DYNAMIC_COLUMN *str, PLAN *plan,
 | 
						|
                                size_t offset_size,
 | 
						|
                                size_t entry_size,
 | 
						|
                                size_t header_size,
 | 
						|
                                size_t new_offset_size,
 | 
						|
                                size_t new_entry_size,
 | 
						|
                                size_t new_header_size,
 | 
						|
                                uint column_count,
 | 
						|
                                uint new_column_count,
 | 
						|
                                uint add_column_count,
 | 
						|
                                uchar *header_end,
 | 
						|
                                size_t max_offset)
 | 
						|
{
 | 
						|
  uchar *write;
 | 
						|
  uchar *header_base= (uchar *)str->str + FIXED_HEADER_SIZE;
 | 
						|
  uint i, j, k;
 | 
						|
  size_t curr_offset;
 | 
						|
 | 
						|
  write= (uchar *)str->str + FIXED_HEADER_SIZE;
 | 
						|
  set_fixed_header(str, (uint)new_offset_size, new_column_count);
 | 
						|
  if (!new_column_count)
 | 
						|
  {
 | 
						|
    // No records left
 | 
						|
    DBUG_ASSERT(new_header_size == 0);
 | 
						|
    str->length= FIXED_HEADER_SIZE;
 | 
						|
    return ER_DYNCOL_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    Move headers first.
 | 
						|
    i= index in array of changes
 | 
						|
    j= index in packed string header index
 | 
						|
  */
 | 
						|
  for (curr_offset= 0, i= 0, j= 0;
 | 
						|
       i < add_column_count || j < column_count;
 | 
						|
       i++)
 | 
						|
  {
 | 
						|
    size_t UNINIT_VAR(first_offset);
 | 
						|
    uint start= j, end;
 | 
						|
 | 
						|
    /*
 | 
						|
      Search in i and j for the next column to add from i and where to
 | 
						|
      add.
 | 
						|
    */
 | 
						|
 | 
						|
    while (i < add_column_count && plan[i].act == PLAN_NOP)
 | 
						|
      i++;                                    /* skip NOP */
 | 
						|
 | 
						|
    if (i == add_column_count)
 | 
						|
      j= end= column_count;
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        old data portion. We don't need to check that j < column_count
 | 
						|
        as plan[i].place is guaranteed to have a pointer inside the
 | 
						|
        data.
 | 
						|
      */
 | 
						|
      while (header_base + j * entry_size < plan[i].place)
 | 
						|
        j++;
 | 
						|
      end= j;
 | 
						|
      if ((plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
 | 
						|
        j++;                              /* data at 'j' will be removed */
 | 
						|
    }
 | 
						|
    plan[i].mv_end= end;
 | 
						|
 | 
						|
    {
 | 
						|
      DYNAMIC_COLUMN_TYPE tp;
 | 
						|
      if (type_and_offset_read_num(&tp, &first_offset,
 | 
						|
                                   header_base + start * entry_size +
 | 
						|
                                   COLUMN_NUMBER_SIZE, offset_size))
 | 
						|
        return ER_DYNCOL_FORMAT;
 | 
						|
    }
 | 
						|
    /* find data to be moved */
 | 
						|
    if (start < end)
 | 
						|
    {
 | 
						|
      size_t data_size=
 | 
						|
        get_length_interval(header_base + start * entry_size,
 | 
						|
                            header_base + end * entry_size,
 | 
						|
                            header_end, offset_size, max_offset);
 | 
						|
      if (data_size == DYNCOL_OFFSET_ERROR ||
 | 
						|
          (long) data_size < 0 ||
 | 
						|
          data_size > max_offset - first_offset)
 | 
						|
      {
 | 
						|
        str->length= 0; // just something valid
 | 
						|
        return ER_DYNCOL_FORMAT;
 | 
						|
      }
 | 
						|
      DBUG_ASSERT(curr_offset == first_offset + plan[i].ddelta);
 | 
						|
      plan[i].mv_offset= first_offset;
 | 
						|
      plan[i].mv_length= data_size;
 | 
						|
      curr_offset+= data_size;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      plan[i].mv_length= 0;
 | 
						|
      plan[i].mv_offset= curr_offset;
 | 
						|
    }
 | 
						|
 | 
						|
    if (plan[i].ddelta == 0 && offset_size == new_offset_size &&
 | 
						|
        plan[i].act != PLAN_DELETE)
 | 
						|
      write+= entry_size * (end - start);
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        Adjust all headers since last loop.
 | 
						|
        We have to do this as the offset for data has moved
 | 
						|
      */
 | 
						|
      for (k= start; k < end; k++)
 | 
						|
      {
 | 
						|
        uchar *read= header_base + k * entry_size;
 | 
						|
        size_t offs;
 | 
						|
        uint nm;
 | 
						|
        DYNAMIC_COLUMN_TYPE tp;
 | 
						|
 | 
						|
        nm= uint2korr(read);                    /* Column nummber */
 | 
						|
        if (type_and_offset_read_num(&tp, &offs, read + COLUMN_NUMBER_SIZE,
 | 
						|
                                     offset_size))
 | 
						|
          return ER_DYNCOL_FORMAT;
 | 
						|
 | 
						|
        if (k > start && offs < first_offset)
 | 
						|
        {
 | 
						|
          str->length= 0; // just something valid
 | 
						|
          return ER_DYNCOL_FORMAT;
 | 
						|
        }
 | 
						|
 | 
						|
        offs+= (size_t) plan[i].ddelta;
 | 
						|
        int2store(write, nm);
 | 
						|
        /* write rest of data at write + COLUMN_NUMBER_SIZE */
 | 
						|
        type_and_offset_store_num(write, new_offset_size, tp, offs);
 | 
						|
        write+= new_entry_size;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /* new data adding */
 | 
						|
    if (i < add_column_count)
 | 
						|
    {
 | 
						|
      if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
 | 
						|
      {
 | 
						|
        int2store(write, *((uint *)plan[i].key));
 | 
						|
        type_and_offset_store_num(write, new_offset_size,
 | 
						|
                                  plan[i].val[0].type,
 | 
						|
                                  curr_offset);
 | 
						|
        write+= new_entry_size;
 | 
						|
        curr_offset+= plan[i].length;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    Move data.
 | 
						|
    i= index in array of changes
 | 
						|
    j= index in packed string header index
 | 
						|
  */
 | 
						|
  str->length= (FIXED_HEADER_SIZE + new_header_size);
 | 
						|
  for (i= 0, j= 0;
 | 
						|
       i < add_column_count || j < column_count;
 | 
						|
       i++)
 | 
						|
  {
 | 
						|
    uint start= j, end;
 | 
						|
 | 
						|
    /*
 | 
						|
      Search in i and j for the next column to add from i and where to
 | 
						|
      add.
 | 
						|
    */
 | 
						|
 | 
						|
    while (i < add_column_count && plan[i].act == PLAN_NOP)
 | 
						|
      i++;                                    /* skip NOP */
 | 
						|
 | 
						|
    j= end= plan[i].mv_end;
 | 
						|
    if (i != add_column_count &&
 | 
						|
        (plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
 | 
						|
      j++;
 | 
						|
 | 
						|
    /* copy first the data that was not replaced in original packed data */
 | 
						|
    if (start < end && plan[i].mv_length)
 | 
						|
    {
 | 
						|
      memmove((header_base + new_header_size +
 | 
						|
               plan[i].mv_offset + plan[i].ddelta),
 | 
						|
              header_base + header_size + plan[i].mv_offset,
 | 
						|
              (size_t) plan[i].mv_length);
 | 
						|
    }
 | 
						|
    str->length+= (size_t) plan[i].mv_length;
 | 
						|
 | 
						|
    /* new data adding */
 | 
						|
    if (i < add_column_count)
 | 
						|
    {
 | 
						|
      if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
 | 
						|
      {
 | 
						|
        data_store(str, plan[i].val, dyncol_fmt_num);/* Append new data */
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef UNUSED
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_update_move_right(DYNAMIC_COLUMN *str, PLAN *plan,
 | 
						|
                                 size_t offset_size,
 | 
						|
                                 size_t entry_size,
 | 
						|
                                 size_t header_size,
 | 
						|
                                 size_t new_offset_size,
 | 
						|
                                 size_t new_entry_size,
 | 
						|
                                 size_t new_header_size,
 | 
						|
                                 uint column_count,
 | 
						|
                                 uint new_column_count,
 | 
						|
                                 uint add_column_count,
 | 
						|
                                 uchar *header_end,
 | 
						|
                                 size_t max_offset)
 | 
						|
{
 | 
						|
  uchar *write;
 | 
						|
  uchar *header_base= (uchar *)str->str + FIXED_HEADER_SIZE;
 | 
						|
  uint i, j, k;
 | 
						|
  size_t curr_offset;
 | 
						|
 | 
						|
  write= (uchar *)str->str + FIXED_HEADER_SIZE;
 | 
						|
  set_fixed_header(str, new_offset_size, new_column_count);
 | 
						|
 | 
						|
  /*
 | 
						|
    Move data first.
 | 
						|
    i= index in array of changes
 | 
						|
    j= index in packed string header index
 | 
						|
  */
 | 
						|
  for (curr_offset= 0, i= 0, j= 0;
 | 
						|
       i < add_column_count || j < column_count;
 | 
						|
       i++)
 | 
						|
  {
 | 
						|
    size_t UNINIT_VAR(first_offset);
 | 
						|
    uint start= j, end;
 | 
						|
 | 
						|
    /*
 | 
						|
      Search in i and j for the next column to add from i and where to
 | 
						|
      add.
 | 
						|
    */
 | 
						|
 | 
						|
    while (i < add_column_count && plan[i].act == PLAN_NOP)
 | 
						|
      i++;                                    /* skip NOP */
 | 
						|
 | 
						|
    if (i == add_column_count)
 | 
						|
      j= end= column_count;
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        old data portion. We don't need to check that j < column_count
 | 
						|
        as plan[i].place is guaranteed to have a pointer inside the
 | 
						|
        data.
 | 
						|
      */
 | 
						|
      while (header_base + j * entry_size < plan[i].place)
 | 
						|
        j++;
 | 
						|
      end= j;
 | 
						|
      if ((plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
 | 
						|
        j++;                              /* data at 'j' will be removed */
 | 
						|
    }
 | 
						|
    plan[i].mv_end= end;
 | 
						|
 | 
						|
    {
 | 
						|
      DYNAMIC_COLUMN_TYPE tp;
 | 
						|
      type_and_offset_read_num(&tp, &first_offset,
 | 
						|
                               header_base +
 | 
						|
                               start * entry_size + COLUMN_NUMBER_SIZE,
 | 
						|
                               offset_size);
 | 
						|
    }
 | 
						|
    /* find data to be moved */
 | 
						|
    if (start < end)
 | 
						|
    {
 | 
						|
      size_t data_size=
 | 
						|
        get_length_interval(header_base + start * entry_size,
 | 
						|
                            header_base + end * entry_size,
 | 
						|
                            header_end, offset_size, max_offset);
 | 
						|
      if (data_size == DYNCOL_OFFSET_ERROR ||
 | 
						|
          (long) data_size < 0 ||
 | 
						|
          data_size > max_offset - first_offset)
 | 
						|
      {
 | 
						|
        str->length= 0; // just something valid
 | 
						|
        return ER_DYNCOL_FORMAT;
 | 
						|
      }
 | 
						|
      DBUG_ASSERT(curr_offset == first_offset + plan[i].ddelta);
 | 
						|
      plan[i].mv_offset= first_offset;
 | 
						|
      plan[i].mv_length= data_size;
 | 
						|
      curr_offset+= data_size;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      plan[i].mv_length= 0;
 | 
						|
      plan[i].mv_offset= curr_offset;
 | 
						|
    }
 | 
						|
 | 
						|
    if (plan[i].ddelta == 0 && offset_size == new_offset_size &&
 | 
						|
        plan[i].act != PLAN_DELETE)
 | 
						|
      write+= entry_size * (end - start);
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        Adjust all headers since last loop.
 | 
						|
        We have to do this as the offset for data has moved
 | 
						|
      */
 | 
						|
      for (k= start; k < end; k++)
 | 
						|
      {
 | 
						|
        uchar *read= header_base + k * entry_size;
 | 
						|
        size_t offs;
 | 
						|
        uint nm;
 | 
						|
        DYNAMIC_COLUMN_TYPE tp;
 | 
						|
 | 
						|
        nm= uint2korr(read);                    /* Column nummber */
 | 
						|
        type_and_offset_read_num(&tp, &offs, read + COLUMN_NUMBER_SIZE,
 | 
						|
                                 offset_size);
 | 
						|
        if (k > start && offs < first_offset)
 | 
						|
        {
 | 
						|
          str->length= 0; // just something valid
 | 
						|
          return ER_DYNCOL_FORMAT;
 | 
						|
        }
 | 
						|
 | 
						|
        offs+= plan[i].ddelta;
 | 
						|
        int2store(write, nm);
 | 
						|
        /* write rest of data at write + COLUMN_NUMBER_SIZE */
 | 
						|
        if (type_and_offset_store_num(write, new_offset_size, tp, offs))
 | 
						|
        {
 | 
						|
          str->length= 0; // just something valid
 | 
						|
          return ER_DYNCOL_FORMAT;
 | 
						|
        }
 | 
						|
        write+= new_entry_size;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /* new data adding */
 | 
						|
    if (i < add_column_count)
 | 
						|
    {
 | 
						|
      if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
 | 
						|
      {
 | 
						|
        int2store(write, *((uint *)plan[i].key));
 | 
						|
        if (type_and_offset_store_num(write, new_offset_size,
 | 
						|
                                      plan[i].val[0].type,
 | 
						|
                                      curr_offset))
 | 
						|
        {
 | 
						|
          str->length= 0; // just something valid
 | 
						|
          return ER_DYNCOL_FORMAT;
 | 
						|
        }
 | 
						|
        write+= new_entry_size;
 | 
						|
        curr_offset+= plan[i].length;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    Move headers.
 | 
						|
    i= index in array of changes
 | 
						|
    j= index in packed string header index
 | 
						|
  */
 | 
						|
  str->length= (FIXED_HEADER_SIZE + new_header_size);
 | 
						|
  for (i= 0, j= 0;
 | 
						|
       i < add_column_count || j < column_count;
 | 
						|
       i++)
 | 
						|
  {
 | 
						|
    uint start= j, end;
 | 
						|
 | 
						|
    /*
 | 
						|
      Search in i and j for the next column to add from i and where to
 | 
						|
      add.
 | 
						|
    */
 | 
						|
 | 
						|
    while (i < add_column_count && plan[i].act == PLAN_NOP)
 | 
						|
      i++;                                    /* skip NOP */
 | 
						|
 | 
						|
    j= end= plan[i].mv_end;
 | 
						|
    if (i != add_column_count &&
 | 
						|
        (plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
 | 
						|
      j++;
 | 
						|
 | 
						|
    /* copy first the data that was not replaced in original packed data */
 | 
						|
    if (start < end && plan[i].mv_length)
 | 
						|
    {
 | 
						|
      memmove((header_base + new_header_size +
 | 
						|
               plan[i].mv_offset + plan[i].ddelta),
 | 
						|
              header_base + header_size + plan[i].mv_offset,
 | 
						|
              plan[i].mv_length);
 | 
						|
    }
 | 
						|
    str->length+= plan[i].mv_length;
 | 
						|
 | 
						|
    /* new data adding */
 | 
						|
    if (i < add_column_count)
 | 
						|
    {
 | 
						|
      if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
 | 
						|
      {
 | 
						|
        data_store(str, plan[i].val, dyncol_fmt_num); /* Append new data */
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
/**
 | 
						|
  Update the packed string with the given columns
 | 
						|
 | 
						|
  @param str             String where to write the data
 | 
						|
  @param add_column_count Number of columns in the arrays
 | 
						|
  @param column_numbers  Array of columns numbers
 | 
						|
  @param values          Array of columns values
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
/* plan allocated on the stack */
 | 
						|
#define IN_PLACE_PLAN 4
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
dynamic_column_update_many(DYNAMIC_COLUMN *str,
 | 
						|
                           uint add_column_count,
 | 
						|
                           uint *column_numbers,
 | 
						|
                           DYNAMIC_COLUMN_VALUE *values)
 | 
						|
{
 | 
						|
  return dynamic_column_update_many_fmt(str, add_column_count, column_numbers,
 | 
						|
                                        values, FALSE);
 | 
						|
}
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_update_many_num(DYNAMIC_COLUMN *str,
 | 
						|
                               uint add_column_count,
 | 
						|
                               uint *column_numbers,
 | 
						|
                               DYNAMIC_COLUMN_VALUE *values)
 | 
						|
{
 | 
						|
  return dynamic_column_update_many_fmt(str, add_column_count, column_numbers,
 | 
						|
                                        values, FALSE);
 | 
						|
}
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_update_many_named(DYNAMIC_COLUMN *str,
 | 
						|
                                 uint add_column_count,
 | 
						|
                                 LEX_STRING *column_names,
 | 
						|
                                 DYNAMIC_COLUMN_VALUE *values)
 | 
						|
{
 | 
						|
  return dynamic_column_update_many_fmt(str, add_column_count, column_names,
 | 
						|
                                        values, TRUE);
 | 
						|
}
 | 
						|
 | 
						|
static uint numlen(uint val)
 | 
						|
{
 | 
						|
  uint res;
 | 
						|
  if (val == 0)
 | 
						|
    return 1;
 | 
						|
  res= 0;
 | 
						|
  while(val)
 | 
						|
  {
 | 
						|
    res++;
 | 
						|
    val/=10;
 | 
						|
  }
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
dynamic_column_update_many_fmt(DYNAMIC_COLUMN *str,
 | 
						|
                               uint add_column_count,
 | 
						|
                               void *column_keys,
 | 
						|
                               DYNAMIC_COLUMN_VALUE *values,
 | 
						|
                               my_bool string_keys)
 | 
						|
{
 | 
						|
  PLAN *plan, *alloc_plan= NULL, in_place_plan[IN_PLACE_PLAN];
 | 
						|
  uchar *element;
 | 
						|
  DYN_HEADER header, new_header;
 | 
						|
  struct st_service_funcs *fmt, *new_fmt;
 | 
						|
  long long data_delta= 0, name_delta= 0;
 | 
						|
  uint i;
 | 
						|
  uint not_null;
 | 
						|
  long long header_delta= 0;
 | 
						|
  long long header_delta_sign, data_delta_sign;
 | 
						|
  int copy= FALSE;
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
  my_bool convert;
 | 
						|
 | 
						|
  if (add_column_count == 0)
 | 
						|
    return ER_DYNCOL_OK;
 | 
						|
 | 
						|
  bzero(&header, sizeof(header));
 | 
						|
  bzero(&new_header, sizeof(new_header));
 | 
						|
  new_header.format= (string_keys ? dyncol_fmt_str : dyncol_fmt_num);
 | 
						|
  new_fmt= fmt_data + new_header.format;
 | 
						|
 | 
						|
  /*
 | 
						|
    Get columns in column order. As the data in 'str' is already
 | 
						|
    in column order this allows to replace all columns in one loop.
 | 
						|
  */
 | 
						|
  if (IN_PLACE_PLAN > add_column_count)
 | 
						|
    plan= in_place_plan;
 | 
						|
  else if (!(alloc_plan= plan=
 | 
						|
             my_malloc(PSI_INSTRUMENT_ME,
 | 
						|
                       sizeof(PLAN) * (add_column_count + 1), MYF(0))))
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
 | 
						|
  not_null= add_column_count;
 | 
						|
  for (i= 0, element= (uchar *) column_keys;
 | 
						|
       i < add_column_count;
 | 
						|
       i++, element+= new_fmt->key_size_in_array)
 | 
						|
  {
 | 
						|
    if ((*new_fmt->check_limit)(&element))
 | 
						|
    {
 | 
						|
      rc= ER_DYNCOL_DATA;
 | 
						|
      goto end;
 | 
						|
    }
 | 
						|
 | 
						|
    plan[i].val= values + i;
 | 
						|
    plan[i].key= element;
 | 
						|
    if (values[i].type == DYN_COL_NULL)
 | 
						|
      not_null--;
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  if (str->length == 0)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Just add new columns. If there was no columns to add we return
 | 
						|
      an empty string.
 | 
						|
     */
 | 
						|
    goto create_new_string;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Check that header is ok */
 | 
						|
  if ((rc= init_read_hdr(&header, str)) < 0)
 | 
						|
    goto end;
 | 
						|
  fmt= fmt_data + header.format;
 | 
						|
  /* new format can't be numeric if the old one is names */
 | 
						|
  DBUG_ASSERT(new_header.format == dyncol_fmt_str ||
 | 
						|
              header.format == dyncol_fmt_num);
 | 
						|
  if (header.column_count == 0)
 | 
						|
    goto create_new_string;
 | 
						|
 | 
						|
  qsort(plan, (size_t)add_column_count, sizeof(PLAN), new_fmt->plan_sort);
 | 
						|
 | 
						|
  new_header.column_count= header.column_count;
 | 
						|
  new_header.nmpool_size= header.nmpool_size;
 | 
						|
  if ((convert= (new_header.format == dyncol_fmt_str &&
 | 
						|
                 header.format == dyncol_fmt_num)))
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(new_header.nmpool_size == 0);
 | 
						|
    for(i= 0, header.entry= header.header;
 | 
						|
        i < header.column_count;
 | 
						|
        i++, header.entry+= header.entry_size)
 | 
						|
    {
 | 
						|
      new_header.nmpool_size+= numlen(uint2korr(header.entry));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (fmt->fixed_hdr + header.header_size + header.nmpool_size > str->length)
 | 
						|
  {
 | 
						|
    rc= ER_DYNCOL_FORMAT;
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    Calculate how many columns and data is added/deleted and make a 'plan'
 | 
						|
    for each of them.
 | 
						|
  */
 | 
						|
  for (i= 0; i < add_column_count; i++)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      For now we don't allow creating two columns with the same number
 | 
						|
      at the time of create.  This can be fixed later to just use the later
 | 
						|
      by comparing the pointers.
 | 
						|
    */
 | 
						|
    if (i < add_column_count - 1 &&
 | 
						|
        new_fmt->column_sort(&plan[i].key, &plan[i + 1].key) == 0)
 | 
						|
    {
 | 
						|
      rc= ER_DYNCOL_DATA;
 | 
						|
      goto end;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Set common variables for all plans */
 | 
						|
    plan[i].ddelta= data_delta;
 | 
						|
    plan[i].ndelta= name_delta;
 | 
						|
    /* get header delta in entries */
 | 
						|
    plan[i].hdelta= header_delta;
 | 
						|
    plan[i].length= 0;                          /* Length if NULL */
 | 
						|
 | 
						|
    if (find_place(&header, plan[i].key, string_keys))
 | 
						|
    {
 | 
						|
      size_t entry_data_size, entry_name_size= 0;
 | 
						|
 | 
						|
      /* Data existed; We have to replace or delete it */
 | 
						|
 | 
						|
      entry_data_size= hdr_interval_length(&header, header.entry +
 | 
						|
                                           header.entry_size);
 | 
						|
      if (entry_data_size == DYNCOL_OFFSET_ERROR ||
 | 
						|
          (long) entry_data_size < 0)
 | 
						|
      {
 | 
						|
        rc= ER_DYNCOL_FORMAT;
 | 
						|
        goto end;
 | 
						|
      }
 | 
						|
 | 
						|
      if (new_header.format == dyncol_fmt_str)
 | 
						|
      {
 | 
						|
        if (header.format == dyncol_fmt_str)
 | 
						|
        {
 | 
						|
          LEX_STRING name;
 | 
						|
          if (read_name(&header, header.entry, &name))
 | 
						|
          {
 | 
						|
            rc= ER_DYNCOL_FORMAT;
 | 
						|
            goto end;
 | 
						|
          }
 | 
						|
          entry_name_size= name.length;
 | 
						|
        }
 | 
						|
        else
 | 
						|
          entry_name_size= numlen(uint2korr(header.entry));
 | 
						|
      }
 | 
						|
 | 
						|
      if (plan[i].val->type == DYN_COL_NULL)
 | 
						|
      {
 | 
						|
        /* Inserting a NULL means delete the old data */
 | 
						|
 | 
						|
        plan[i].act= PLAN_DELETE;	        /* Remove old value */
 | 
						|
        header_delta--;                         /* One row less in header */
 | 
						|
        data_delta-= entry_data_size;           /* Less data to store */
 | 
						|
        name_delta-= entry_name_size;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        /* Replace the value */
 | 
						|
 | 
						|
        plan[i].act= PLAN_REPLACE;
 | 
						|
        /* get data delta in bytes */
 | 
						|
        if ((plan[i].length= dynamic_column_value_len(plan[i].val,
 | 
						|
                                                      new_header.format)) ==
 | 
						|
            (size_t) ~0)
 | 
						|
        {
 | 
						|
          rc= ER_DYNCOL_DATA;
 | 
						|
          goto end;
 | 
						|
        }
 | 
						|
        data_delta+= plan[i].length - entry_data_size;
 | 
						|
        if (new_header.format == dyncol_fmt_str)
 | 
						|
        {
 | 
						|
          name_delta+= ((LEX_STRING *)(plan[i].key))->length - entry_name_size;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /* Data did not exists. Add if it it's not NULL */
 | 
						|
 | 
						|
      if (plan[i].val->type == DYN_COL_NULL)
 | 
						|
      {
 | 
						|
        plan[i].act= PLAN_NOP;                  /* Mark entry to be skipped */
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        /* Add new value */
 | 
						|
 | 
						|
        plan[i].act= PLAN_ADD;
 | 
						|
        header_delta++;                         /* One more row in header */
 | 
						|
        /* get data delta in bytes */
 | 
						|
        if ((plan[i].length= dynamic_column_value_len(plan[i].val,
 | 
						|
                                                      new_header.format)) ==
 | 
						|
            (size_t) ~0)
 | 
						|
        {
 | 
						|
          rc= ER_DYNCOL_DATA;
 | 
						|
          goto end;
 | 
						|
        }
 | 
						|
        data_delta+= plan[i].length;
 | 
						|
        if (new_header.format == dyncol_fmt_str)
 | 
						|
          name_delta+= ((LEX_STRING *)plan[i].key)->length;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    plan[i].place= header.entry;
 | 
						|
  }
 | 
						|
  plan[add_column_count].hdelta= header_delta;
 | 
						|
  plan[add_column_count].ddelta= data_delta;
 | 
						|
  plan[add_column_count].act= PLAN_NOP;
 | 
						|
  plan[add_column_count].place= header.dtpool;
 | 
						|
 | 
						|
  new_header.column_count= (uint)(header.column_count + header_delta);
 | 
						|
 | 
						|
  /*
 | 
						|
    Check if it is only "increasing" or only "decreasing" plan for (header
 | 
						|
    and data separately).
 | 
						|
  */
 | 
						|
  new_header.data_size= (size_t) (header.data_size + data_delta);
 | 
						|
  new_header.nmpool_size= (size_t) (new_header.nmpool_size + name_delta);
 | 
						|
  DBUG_ASSERT(new_header.format != dyncol_fmt_num ||
 | 
						|
              new_header.nmpool_size == 0);
 | 
						|
  if ((new_header.offset_size=
 | 
						|
       new_fmt->dynamic_column_offset_bytes(new_header.data_size)) >=
 | 
						|
      new_fmt->max_offset_size)
 | 
						|
  {
 | 
						|
    rc= ER_DYNCOL_LIMIT;
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
 | 
						|
  copy= ((header.format != new_header.format) ||
 | 
						|
         (new_header.format == dyncol_fmt_str));
 | 
						|
  /* if (new_header.offset_size!=offset_size) then we have to rewrite header */
 | 
						|
  header_delta_sign=
 | 
						|
    ((int)new_header.offset_size + new_fmt->fixed_hdr_entry) -
 | 
						|
    ((int)header.offset_size + fmt->fixed_hdr_entry);
 | 
						|
  data_delta_sign= 0;
 | 
						|
  // plan[add_column_count] contains last deltas.
 | 
						|
  for (i= 0; i <= add_column_count && !copy; i++)
 | 
						|
  {
 | 
						|
    /* This is the check for increasing/decreasing */
 | 
						|
    DELTA_CHECK(header_delta_sign, plan[i].hdelta, copy);
 | 
						|
    DELTA_CHECK(data_delta_sign, plan[i].ddelta, copy);
 | 
						|
  }
 | 
						|
  calc_param(&new_header.entry_size, &new_header.header_size,
 | 
						|
             new_fmt->fixed_hdr_entry,
 | 
						|
             new_header.offset_size, new_header.column_count);
 | 
						|
 | 
						|
  /*
 | 
						|
    Need copy because:
 | 
						|
    1, Header/data parts moved in different directions.
 | 
						|
    2. There is no enough allocated space in the string.
 | 
						|
    3. Header and data moved in different directions.
 | 
						|
  */
 | 
						|
  if (copy || /*1.*/
 | 
						|
      str->max_length < str->length + header_delta + data_delta || /*2.*/
 | 
						|
      ((header_delta_sign < 0 && data_delta_sign > 0) ||
 | 
						|
       (header_delta_sign > 0 && data_delta_sign < 0))) /*3.*/
 | 
						|
    rc= dynamic_column_update_copy(str, plan, add_column_count,
 | 
						|
                                   &header, &new_header,
 | 
						|
                                   convert);
 | 
						|
  else
 | 
						|
    if (header_delta_sign < 0)
 | 
						|
      rc= dynamic_column_update_move_left(str, plan, header.offset_size,
 | 
						|
                                          header.entry_size,
 | 
						|
                                          header.header_size,
 | 
						|
                                          new_header.offset_size,
 | 
						|
                                          new_header.entry_size,
 | 
						|
                                          new_header.header_size,
 | 
						|
                                          header.column_count,
 | 
						|
                                          new_header.column_count,
 | 
						|
                                          add_column_count, header.dtpool,
 | 
						|
                                          header.data_size);
 | 
						|
    else
 | 
						|
      /*
 | 
						|
      rc= dynamic_column_update_move_right(str, plan, offset_size,
 | 
						|
                                           entry_size,  header_size,
 | 
						|
                                           new_header.offset_size,
 | 
						|
                                           new_header.entry_size,
 | 
						|
                                           new_heder.header_size, column_count,
 | 
						|
                                           new_header.column_count,
 | 
						|
                                           add_column_count, header_end,
 | 
						|
                                           header.data_size);
 | 
						|
                                         */
 | 
						|
      rc= dynamic_column_update_copy(str, plan, add_column_count,
 | 
						|
                                     &header, &new_header,
 | 
						|
                                     convert);
 | 
						|
end:
 | 
						|
  my_free(alloc_plan);
 | 
						|
  return rc;
 | 
						|
 | 
						|
create_new_string:
 | 
						|
  /* There is no columns from before, so let's just add the new ones */
 | 
						|
  rc= ER_DYNCOL_OK;
 | 
						|
  if (not_null != 0)
 | 
						|
    rc= dynamic_column_create_many_internal_fmt(str, add_column_count,
 | 
						|
                                                (uint*)column_keys, values,
 | 
						|
                                                str->str == NULL,
 | 
						|
                                                string_keys);
 | 
						|
  goto end;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Update the packed string with the given column
 | 
						|
 | 
						|
  @param str             String where to write the data
 | 
						|
  @param column_number   Array of columns number
 | 
						|
  @param values          Array of columns values
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
dynamic_column_update(DYNAMIC_COLUMN *str, uint column_nr,
 | 
						|
                      DYNAMIC_COLUMN_VALUE *value)
 | 
						|
{
 | 
						|
  return dynamic_column_update_many(str, 1, &column_nr, value);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_check(DYNAMIC_COLUMN *str)
 | 
						|
{
 | 
						|
  struct st_service_funcs *fmt;
 | 
						|
  enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT;
 | 
						|
  DYN_HEADER header;
 | 
						|
  uint i;
 | 
						|
  size_t data_offset= 0, name_offset= 0;
 | 
						|
  size_t prev_data_offset= 0, prev_name_offset= 0;
 | 
						|
  LEX_STRING name= {0,0}, prev_name= {0,0};
 | 
						|
  uint num= 0, prev_num= 0;
 | 
						|
  void *key, *prev_key;
 | 
						|
  enum enum_dynamic_column_type type= DYN_COL_NULL, prev_type= DYN_COL_NULL;
 | 
						|
 | 
						|
  DBUG_ENTER("dynamic_column_check");
 | 
						|
 | 
						|
  if (str->length == 0)
 | 
						|
  {
 | 
						|
    DBUG_PRINT("info", ("empty string is OK"));
 | 
						|
    DBUG_RETURN(ER_DYNCOL_OK);
 | 
						|
  }
 | 
						|
 | 
						|
  bzero(&header, sizeof(header));
 | 
						|
 | 
						|
  /* Check that header is OK */
 | 
						|
  if (read_fixed_header(&header, str))
 | 
						|
  {
 | 
						|
    DBUG_PRINT("info", ("Reading fixed string header failed"));
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
  fmt= fmt_data + header.format;
 | 
						|
  calc_param(&header.entry_size, &header.header_size,
 | 
						|
             fmt->fixed_hdr_entry, header.offset_size,
 | 
						|
             header.column_count);
 | 
						|
  /* headers are out of string length (no space for data and part of headers) */
 | 
						|
  if (fmt->fixed_hdr + header.header_size + header.nmpool_size > str->length)
 | 
						|
  {
 | 
						|
    DBUG_PRINT("info", ("Fixed header: %u  Header size: %u  "
 | 
						|
                        "Name pool size: %u  but Strig length: %u",
 | 
						|
                        (uint)fmt->fixed_hdr,
 | 
						|
                        (uint)header.header_size,
 | 
						|
                        (uint)header.nmpool_size,
 | 
						|
                        (uint)str->length));
 | 
						|
    goto end;
 | 
						|
  }
 | 
						|
  header.header= (uchar*)str->str + fmt->fixed_hdr;
 | 
						|
  header.nmpool= header.header + header.header_size;
 | 
						|
  header.dtpool= header.nmpool + header.nmpool_size;
 | 
						|
  header.data_size= str->length - fmt->fixed_hdr -
 | 
						|
    header.header_size - header.nmpool_size;
 | 
						|
 | 
						|
  /* read and check headers */
 | 
						|
  if (header.format == dyncol_fmt_num)
 | 
						|
  {
 | 
						|
    key= #
 | 
						|
    prev_key= &prev_num;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    key= &name;
 | 
						|
    prev_key= &prev_name;
 | 
						|
  }
 | 
						|
  for (i= 0, header.entry= header.header;
 | 
						|
       i < header.column_count;
 | 
						|
       i++, header.entry+= header.entry_size)
 | 
						|
  {
 | 
						|
 | 
						|
    if (header.format == dyncol_fmt_num)
 | 
						|
    {
 | 
						|
       num= uint2korr(header.entry);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      DBUG_ASSERT(header.format == dyncol_fmt_str);
 | 
						|
      if (read_name(&header, header.entry, &name))
 | 
						|
      {
 | 
						|
        DBUG_PRINT("info", ("Reading name failed: Field order: %u"
 | 
						|
                            "  Name offset: %u"
 | 
						|
                            "  Name pool size: %u",
 | 
						|
                            (uint) i,
 | 
						|
                            uint2korr(header.entry),
 | 
						|
                            (uint)header.nmpool_size));
 | 
						|
        goto end;
 | 
						|
      }
 | 
						|
      name_offset= name.str - (char *)header.nmpool;
 | 
						|
    }
 | 
						|
    if ((*fmt->type_and_offset_read)(&type, &data_offset,
 | 
						|
                                     header.entry + fmt->fixed_hdr_entry,
 | 
						|
                                     header.offset_size))
 | 
						|
      goto end;
 | 
						|
 | 
						|
    DBUG_ASSERT(type != DYN_COL_NULL);
 | 
						|
    if (data_offset > header.data_size)
 | 
						|
    {
 | 
						|
      DBUG_PRINT("info", ("Field order: %u  Data offset: %u"
 | 
						|
                          " > Data pool size: %u",
 | 
						|
                          (uint)i,
 | 
						|
                          (uint)data_offset,
 | 
						|
                          (uint)header.data_size));
 | 
						|
      goto end;
 | 
						|
    }
 | 
						|
    if (prev_type != DYN_COL_NULL)
 | 
						|
    {
 | 
						|
      /* It is not first entry */
 | 
						|
      if (prev_data_offset > data_offset ||
 | 
						|
          ((prev_type !=  DYN_COL_INT &&
 | 
						|
            prev_type != DYN_COL_UINT &&
 | 
						|
            prev_type != DYN_COL_DECIMAL) && prev_data_offset == data_offset))
 | 
						|
      {
 | 
						|
        DBUG_PRINT("info", ("Field order: %u  Previous data offset: %u"
 | 
						|
                            " >(=) Current data offset: %u",
 | 
						|
                            (uint)i,
 | 
						|
                            (uint)prev_data_offset,
 | 
						|
                            (uint)data_offset));
 | 
						|
        goto end;
 | 
						|
      }
 | 
						|
      if (prev_name_offset > name_offset)
 | 
						|
      {
 | 
						|
        DBUG_PRINT("info", ("Field order: %u  Previous name offset: %u"
 | 
						|
                            " > Current name offset: %u",
 | 
						|
                            (uint)i,
 | 
						|
                            (uint)prev_data_offset,
 | 
						|
                            (uint)data_offset));
 | 
						|
        goto end;
 | 
						|
      }
 | 
						|
      if ((*fmt->column_sort)(&prev_key, &key) >= 0)
 | 
						|
      {
 | 
						|
        DBUG_PRINT("info", ("Field order: %u  Previous key >= Current key",
 | 
						|
                            (uint)i));
 | 
						|
        goto end;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    prev_num= num;
 | 
						|
    prev_name= name;
 | 
						|
    prev_data_offset= data_offset;
 | 
						|
    prev_name_offset= name_offset;
 | 
						|
    prev_type= type;
 | 
						|
  }
 | 
						|
 | 
						|
  /* check data, which we can */
 | 
						|
  for (i= 0, header.entry= header.header;
 | 
						|
       i < header.column_count;
 | 
						|
       i++, header.entry+= header.entry_size)
 | 
						|
  {
 | 
						|
    DYNAMIC_COLUMN_VALUE store;
 | 
						|
    // already checked by previouse pass
 | 
						|
    (*fmt->type_and_offset_read)(&header.type, &header.offset,
 | 
						|
                                 header.entry + fmt->fixed_hdr_entry,
 | 
						|
                                 header.offset_size);
 | 
						|
    header.length=
 | 
						|
      hdr_interval_length(&header, header.entry + header.entry_size);
 | 
						|
    header.data= header.dtpool + header.offset;
 | 
						|
    switch ((header.type)) {
 | 
						|
    case DYN_COL_INT:
 | 
						|
      rc= dynamic_column_sint_read(&store, header.data, header.length);
 | 
						|
      break;
 | 
						|
    case DYN_COL_UINT:
 | 
						|
      rc= dynamic_column_uint_read(&store, header.data, header.length);
 | 
						|
      break;
 | 
						|
    case DYN_COL_DOUBLE:
 | 
						|
      rc= dynamic_column_double_read(&store, header.data, header.length);
 | 
						|
      break;
 | 
						|
    case DYN_COL_STRING:
 | 
						|
      rc= dynamic_column_string_read(&store, header.data, header.length);
 | 
						|
      break;
 | 
						|
    case DYN_COL_DECIMAL:
 | 
						|
      rc= dynamic_column_decimal_read(&store, header.data, header.length);
 | 
						|
      break;
 | 
						|
    case DYN_COL_DATETIME:
 | 
						|
      rc= dynamic_column_date_time_read(&store, header.data,
 | 
						|
                                        header.length);
 | 
						|
      break;
 | 
						|
    case DYN_COL_DATE:
 | 
						|
      rc= dynamic_column_date_read(&store, header.data, header.length);
 | 
						|
      break;
 | 
						|
    case DYN_COL_TIME:
 | 
						|
      rc= dynamic_column_time_read(&store, header.data, header.length);
 | 
						|
      break;
 | 
						|
    case DYN_COL_DYNCOL:
 | 
						|
      rc= dynamic_column_dyncol_read(&store, header.data, header.length);
 | 
						|
      break;
 | 
						|
    case DYN_COL_NULL:
 | 
						|
    default:
 | 
						|
      rc= ER_DYNCOL_FORMAT;
 | 
						|
      goto end;
 | 
						|
    }
 | 
						|
    if (rc != ER_DYNCOL_OK)
 | 
						|
    {
 | 
						|
      DBUG_ASSERT(rc < 0);
 | 
						|
      DBUG_PRINT("info", ("Field order: %u  Can't read data: %i",
 | 
						|
                          (uint)i, (int) rc));
 | 
						|
      goto end;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  rc= ER_DYNCOL_OK;
 | 
						|
end:
 | 
						|
  DBUG_RETURN(rc);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
my_bool dynstr_append_json_quoted(DYNAMIC_STRING *str,
 | 
						|
                                  const char *append, size_t len)
 | 
						|
{
 | 
						|
  size_t additional= ((str->alloc_increment && str->alloc_increment > 6) ?
 | 
						|
                    str->alloc_increment :
 | 
						|
                    10);
 | 
						|
  size_t lim= additional;
 | 
						|
  size_t i;
 | 
						|
  if (dynstr_realloc(str, len + additional + 2))
 | 
						|
    return TRUE;
 | 
						|
  str->str[str->length++]= '"';
 | 
						|
  for (i= 0; i < len; i++)
 | 
						|
  {
 | 
						|
    register char c= append[i];
 | 
						|
    if (unlikely(((uchar)c) <= 0x1F))
 | 
						|
    {
 | 
						|
      if (lim < 6)
 | 
						|
        {
 | 
						|
          if (dynstr_realloc(str, additional))
 | 
						|
            return TRUE;
 | 
						|
          lim+= additional;
 | 
						|
        }
 | 
						|
        lim -= 6;
 | 
						|
        str->str[str->length++]= '\\';
 | 
						|
        str->str[str->length++]= 'u';
 | 
						|
        str->str[str->length++]= '0';
 | 
						|
        str->str[str->length++]= '0';
 | 
						|
        str->str[str->length++]= (c < 0x10 ? '0' : '1');
 | 
						|
        c%= 0x10;
 | 
						|
        str->str[str->length++]= (c < 0xA ? '0' + c  : 'A' + (c - 0xA));
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if (lim < 2)
 | 
						|
      {
 | 
						|
        if (dynstr_realloc(str, additional))
 | 
						|
          return TRUE;
 | 
						|
        lim += additional;
 | 
						|
      }
 | 
						|
      if (c == '"' || c == '\\')
 | 
						|
      {
 | 
						|
        lim--;
 | 
						|
        str->str[str->length++]= '\\';
 | 
						|
      }
 | 
						|
      lim--;
 | 
						|
      str->str[str->length++]= c;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  str->str[str->length++]= '"';
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_val_str(DYNAMIC_STRING *str, DYNAMIC_COLUMN_VALUE *val,
 | 
						|
                       CHARSET_INFO *cs, char quote)
 | 
						|
{
 | 
						|
  char buff[40];
 | 
						|
  size_t len;
 | 
						|
  switch (val->type) {
 | 
						|
    case DYN_COL_INT:
 | 
						|
      len= snprintf(buff, sizeof(buff), "%lld", val->x.long_value);
 | 
						|
      if (dynstr_append_mem(str, buff, len))
 | 
						|
        return ER_DYNCOL_RESOURCE;
 | 
						|
      break;
 | 
						|
    case DYN_COL_UINT:
 | 
						|
      len= snprintf(buff, sizeof(buff), "%llu", val->x.ulong_value);
 | 
						|
      if (dynstr_append_mem(str, buff, len))
 | 
						|
        return ER_DYNCOL_RESOURCE;
 | 
						|
      break;
 | 
						|
    case DYN_COL_DOUBLE:
 | 
						|
 | 
						|
      len= my_gcvt(val->x.double_value, MY_GCVT_ARG_DOUBLE,
 | 
						|
                   sizeof(buff) - 1, buff, NULL);
 | 
						|
      if (dynstr_realloc(str, len + (quote ? 2 : 0)))
 | 
						|
        return ER_DYNCOL_RESOURCE;
 | 
						|
      dynstr_append_mem(str, buff, len);
 | 
						|
      break;
 | 
						|
    case DYN_COL_DYNCOL:
 | 
						|
    case DYN_COL_STRING:
 | 
						|
      {
 | 
						|
        char *alloc= NULL;
 | 
						|
        char *from= val->x.string.value.str;
 | 
						|
        ulong bufflen;
 | 
						|
        my_bool conv= !my_charset_same(val->x.string.charset, cs);
 | 
						|
        my_bool rc;
 | 
						|
        len= val->x.string.value.length;
 | 
						|
        bufflen= (ulong)(len * (conv ? cs->mbmaxlen : 1));
 | 
						|
        if (dynstr_realloc(str, bufflen))
 | 
						|
            return ER_DYNCOL_RESOURCE;
 | 
						|
 | 
						|
        // guaranty UTF-8 string for value
 | 
						|
        if (!my_charset_same(val->x.string.charset, cs))
 | 
						|
        {
 | 
						|
          uint dummy_errors;
 | 
						|
          if (!quote)
 | 
						|
          {
 | 
						|
            /* convert to the destination */
 | 
						|
            str->length+= my_convert(str->str, bufflen,
 | 
						|
                                     cs,
 | 
						|
                                     from, (uint32)len,
 | 
						|
                                     val->x.string.charset,
 | 
						|
                                     &dummy_errors);
 | 
						|
            return ER_DYNCOL_OK;
 | 
						|
          }
 | 
						|
          if ((alloc= (char *)my_malloc(PSI_INSTRUMENT_ME, bufflen, MYF(0))))
 | 
						|
          {
 | 
						|
            len= my_convert(alloc, bufflen, cs, from, (uint32)len,
 | 
						|
                            val->x.string.charset, &dummy_errors);
 | 
						|
            from= alloc;
 | 
						|
          }
 | 
						|
          else
 | 
						|
            return ER_DYNCOL_RESOURCE;
 | 
						|
        }
 | 
						|
        if (quote)
 | 
						|
          if (quote == DYNCOL_JSON_ESC)
 | 
						|
            rc= dynstr_append_json_quoted(str, from, len);
 | 
						|
          else
 | 
						|
            rc= dynstr_append_quoted(str, from, len, quote);
 | 
						|
        else
 | 
						|
          rc= dynstr_append_mem(str, from, len);
 | 
						|
        if (alloc)
 | 
						|
          my_free(alloc);
 | 
						|
        if (rc)
 | 
						|
          return ER_DYNCOL_RESOURCE;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    case DYN_COL_DECIMAL:
 | 
						|
      {
 | 
						|
        int tmp_len= sizeof(buff);
 | 
						|
        decimal2string(&val->x.decimal.value, buff, &tmp_len,
 | 
						|
                       0, val->x.decimal.value.frac,
 | 
						|
                       '0');
 | 
						|
        if (dynstr_append_mem(str, buff, tmp_len))
 | 
						|
          return ER_DYNCOL_RESOURCE;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    case DYN_COL_DATETIME:
 | 
						|
    case DYN_COL_DATE:
 | 
						|
    case DYN_COL_TIME:
 | 
						|
      len= my_TIME_to_str(&val->x.time_value, buff, AUTO_SEC_PART_DIGITS);
 | 
						|
      if (dynstr_realloc(str, len + (quote ? 2 : 0)))
 | 
						|
        return ER_DYNCOL_RESOURCE;
 | 
						|
      if (quote)
 | 
						|
        str->str[str->length++]= '"';
 | 
						|
      dynstr_append_mem(str, buff, len);
 | 
						|
      if (quote)
 | 
						|
        str->str[str->length++]= '"';
 | 
						|
      break;
 | 
						|
    case DYN_COL_NULL:
 | 
						|
      if (dynstr_append_mem(str, "null", 4))
 | 
						|
        return ER_DYNCOL_RESOURCE;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      return(ER_DYNCOL_FORMAT);
 | 
						|
  }
 | 
						|
  return(ER_DYNCOL_OK);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_val_long(longlong *ll, DYNAMIC_COLUMN_VALUE *val)
 | 
						|
{
 | 
						|
  enum enum_dyncol_func_result rc= ER_DYNCOL_OK;
 | 
						|
  *ll= 0;
 | 
						|
  switch (val->type) {
 | 
						|
  case DYN_COL_INT:
 | 
						|
      *ll= val->x.long_value;
 | 
						|
      break;
 | 
						|
    case DYN_COL_UINT:
 | 
						|
      *ll= (longlong)val->x.ulong_value;
 | 
						|
      if (val->x.ulong_value > ULONGLONG_MAX)
 | 
						|
         rc= ER_DYNCOL_TRUNCATED;
 | 
						|
      break;
 | 
						|
    case DYN_COL_DOUBLE:
 | 
						|
      *ll= (longlong)val->x.double_value;
 | 
						|
      if (((double) *ll) != val->x.double_value)
 | 
						|
        rc= ER_DYNCOL_TRUNCATED;
 | 
						|
      break;
 | 
						|
    case DYN_COL_STRING:
 | 
						|
      {
 | 
						|
        char *src= val->x.string.value.str;
 | 
						|
        size_t len= val->x.string.value.length;
 | 
						|
        longlong i= 0, sign= 1;
 | 
						|
 | 
						|
        while (len && my_isspace(&my_charset_latin1, *src)) src++,len--;
 | 
						|
 | 
						|
        if (len)
 | 
						|
        {
 | 
						|
          if (*src == '-')
 | 
						|
          {
 | 
						|
            sign= -1;
 | 
						|
            src++;
 | 
						|
          } else if (*src == '+')
 | 
						|
            src++;
 | 
						|
          while(len && my_isdigit(&my_charset_latin1, *src))
 | 
						|
          {
 | 
						|
            i= i * 10 + (*src - '0');
 | 
						|
            src++;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        else
 | 
						|
          rc= ER_DYNCOL_TRUNCATED;
 | 
						|
        if (len)
 | 
						|
          rc= ER_DYNCOL_TRUNCATED;
 | 
						|
        *ll= i * sign;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    case DYN_COL_DECIMAL:
 | 
						|
      if (decimal2longlong(&val->x.decimal.value, ll) != E_DEC_OK)
 | 
						|
        rc= ER_DYNCOL_TRUNCATED;
 | 
						|
      break;
 | 
						|
    case DYN_COL_DATETIME:
 | 
						|
      *ll= (val->x.time_value.year * 10000000000ull +
 | 
						|
            val->x.time_value.month * 100000000L +
 | 
						|
            val->x.time_value.day * 1000000 +
 | 
						|
            val->x.time_value.hour * 10000 +
 | 
						|
            val->x.time_value.minute * 100 +
 | 
						|
            val->x.time_value.second) *
 | 
						|
        (val->x.time_value.neg ? -1 : 1);
 | 
						|
      break;
 | 
						|
    case DYN_COL_DATE:
 | 
						|
      *ll= (val->x.time_value.year * 10000 +
 | 
						|
            val->x.time_value.month * 100 +
 | 
						|
            val->x.time_value.day) *
 | 
						|
        (val->x.time_value.neg ? -1 : 1);
 | 
						|
      break;
 | 
						|
    case DYN_COL_TIME:
 | 
						|
      *ll= (val->x.time_value.hour * 10000 +
 | 
						|
            val->x.time_value.minute * 100 +
 | 
						|
            val->x.time_value.second) *
 | 
						|
        (val->x.time_value.neg ? -1 : 1);
 | 
						|
      break;
 | 
						|
    case DYN_COL_DYNCOL:
 | 
						|
    case DYN_COL_NULL:
 | 
						|
      rc= ER_DYNCOL_TRUNCATED;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      return(ER_DYNCOL_FORMAT);
 | 
						|
  }
 | 
						|
  return(rc);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_val_double(double *dbl, DYNAMIC_COLUMN_VALUE *val)
 | 
						|
{
 | 
						|
  enum enum_dyncol_func_result rc= ER_DYNCOL_OK;
 | 
						|
  *dbl= 0;
 | 
						|
  switch (val->type) {
 | 
						|
  case DYN_COL_INT:
 | 
						|
      *dbl= (double)val->x.long_value;
 | 
						|
      if (((longlong) *dbl) != val->x.long_value)
 | 
						|
        rc= ER_DYNCOL_TRUNCATED;
 | 
						|
      break;
 | 
						|
    case DYN_COL_UINT:
 | 
						|
      *dbl= (double)val->x.ulong_value;
 | 
						|
      if (((ulonglong) *dbl) != val->x.ulong_value)
 | 
						|
        rc= ER_DYNCOL_TRUNCATED;
 | 
						|
      break;
 | 
						|
    case DYN_COL_DOUBLE:
 | 
						|
      *dbl= val->x.double_value;
 | 
						|
      break;
 | 
						|
    case DYN_COL_STRING:
 | 
						|
      {
 | 
						|
        char *str, *end;
 | 
						|
        if (!(str= malloc(val->x.string.value.length + 1)))
 | 
						|
          return ER_DYNCOL_RESOURCE;
 | 
						|
        memcpy(str, val->x.string.value.str, val->x.string.value.length);
 | 
						|
        str[val->x.string.value.length]= '\0';
 | 
						|
        *dbl= strtod(str, &end);
 | 
						|
        if (*end != '\0')
 | 
						|
          rc= ER_DYNCOL_TRUNCATED;
 | 
						|
        free(str);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    case DYN_COL_DECIMAL:
 | 
						|
      if (decimal2double(&val->x.decimal.value, dbl) != E_DEC_OK)
 | 
						|
        rc= ER_DYNCOL_TRUNCATED;
 | 
						|
      break;
 | 
						|
    case DYN_COL_DATETIME:
 | 
						|
      *dbl= (double)(val->x.time_value.year * 10000000000ull +
 | 
						|
                     val->x.time_value.month * 100000000L +
 | 
						|
                     val->x.time_value.day * 1000000 +
 | 
						|
                     val->x.time_value.hour * 10000 +
 | 
						|
                     val->x.time_value.minute * 100 +
 | 
						|
                     val->x.time_value.second) *
 | 
						|
        (val->x.time_value.neg ? -1 : 1);
 | 
						|
      break;
 | 
						|
    case DYN_COL_DATE:
 | 
						|
      *dbl= (double)(val->x.time_value.year * 10000 +
 | 
						|
                     val->x.time_value.month * 100 +
 | 
						|
                     val->x.time_value.day) *
 | 
						|
        (val->x.time_value.neg ? -1 : 1);
 | 
						|
      break;
 | 
						|
    case DYN_COL_TIME:
 | 
						|
      *dbl= (double)(val->x.time_value.hour * 10000 +
 | 
						|
                     val->x.time_value.minute * 100 +
 | 
						|
                     val->x.time_value.second) *
 | 
						|
        (val->x.time_value.neg ? -1 : 1);
 | 
						|
      break;
 | 
						|
    case DYN_COL_DYNCOL:
 | 
						|
    case DYN_COL_NULL:
 | 
						|
      rc= ER_DYNCOL_TRUNCATED;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      return(ER_DYNCOL_FORMAT);
 | 
						|
  }
 | 
						|
  return(rc);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Convert to JSON
 | 
						|
 | 
						|
  @param str             The packed string
 | 
						|
  @param json            Where to put json result
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
#define JSON_STACK_PROTECTION 10
 | 
						|
 | 
						|
static enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_json_internal(DYNAMIC_COLUMN *str, DYNAMIC_STRING *json,
 | 
						|
                             uint lvl)
 | 
						|
{
 | 
						|
  DYN_HEADER header;
 | 
						|
  uint i;
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
 | 
						|
  if (lvl >= JSON_STACK_PROTECTION)
 | 
						|
  {
 | 
						|
    rc= ER_DYNCOL_RESOURCE;
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  if (str->length == 0)
 | 
						|
    return ER_DYNCOL_OK;                        /* no columns */
 | 
						|
 | 
						|
  if ((rc= init_read_hdr(&header, str)) < 0)
 | 
						|
    goto err;
 | 
						|
 | 
						|
  if (header.entry_size * header.column_count + FIXED_HEADER_SIZE >
 | 
						|
      str->length)
 | 
						|
  {
 | 
						|
    rc= ER_DYNCOL_FORMAT;
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
 | 
						|
  rc= ER_DYNCOL_RESOURCE;
 | 
						|
 | 
						|
  if (dynstr_append_mem(json, "{", 1))
 | 
						|
    goto err;
 | 
						|
  for (i= 0, header.entry= header.header;
 | 
						|
       i < header.column_count;
 | 
						|
       i++, header.entry+= header.entry_size)
 | 
						|
  {
 | 
						|
    DYNAMIC_COLUMN_VALUE val;
 | 
						|
    if (i != 0 && dynstr_append_mem(json, ",", 1))
 | 
						|
      goto err;
 | 
						|
    header.length=
 | 
						|
      hdr_interval_length(&header, header.entry + header.entry_size);
 | 
						|
    header.data= header.dtpool + header.offset;
 | 
						|
    /*
 | 
						|
      Check that the found data is within the ranges. This can happen if
 | 
						|
      we get data with wrong offsets.
 | 
						|
    */
 | 
						|
    if (header.length == DYNCOL_OFFSET_ERROR ||
 | 
						|
        header.length > INT_MAX || header.offset > header.data_size)
 | 
						|
    {
 | 
						|
      rc= ER_DYNCOL_FORMAT;
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
    if ((rc= dynamic_column_get_value(&header, &val)) < 0)
 | 
						|
      goto err;
 | 
						|
    if (header.format == dyncol_fmt_num)
 | 
						|
    {
 | 
						|
      uint nm= uint2korr(header.entry);
 | 
						|
      if (dynstr_realloc(json, DYNCOL_NUM_CHAR + 3))
 | 
						|
        goto err;
 | 
						|
      json->str[json->length++]= '"';
 | 
						|
      json->length+= (snprintf(json->str + json->length,
 | 
						|
                               DYNCOL_NUM_CHAR, "%u", nm));
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      LEX_STRING name;
 | 
						|
      if (read_name(&header, header.entry, &name))
 | 
						|
      {
 | 
						|
        rc= ER_DYNCOL_FORMAT;
 | 
						|
        goto err;
 | 
						|
      }
 | 
						|
      if (dynstr_realloc(json, name.length + 3))
 | 
						|
        goto err;
 | 
						|
      json->str[json->length++]= '"';
 | 
						|
      memcpy(json->str + json->length, name.str, name.length);
 | 
						|
      json->length+= name.length;
 | 
						|
    }
 | 
						|
    json->str[json->length++]= '"';
 | 
						|
    json->str[json->length++]= ':';
 | 
						|
    if (val.type == DYN_COL_DYNCOL)
 | 
						|
    {
 | 
						|
      /* here we use it only for read so can cheat a bit */
 | 
						|
      DYNAMIC_COLUMN dc;
 | 
						|
      bzero(&dc, sizeof(dc));
 | 
						|
      dc.str= val.x.string.value.str;
 | 
						|
      dc.length= val.x.string.value.length;
 | 
						|
      if (mariadb_dyncol_json_internal(&dc, json, lvl + 1) < 0)
 | 
						|
      {
 | 
						|
        dc.str= NULL; dc.length= 0;
 | 
						|
        goto err;
 | 
						|
      }
 | 
						|
      dc.str= NULL; dc.length= 0;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if ((rc= mariadb_dyncol_val_str(json, &val, DYNCOL_UTF, DYNCOL_JSON_ESC))
 | 
						|
           < 0)
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (dynstr_append_mem(json, "}", 1))
 | 
						|
  {
 | 
						|
    rc= ER_DYNCOL_RESOURCE;
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
 | 
						|
err:
 | 
						|
  json->length= 0;
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_json(DYNAMIC_COLUMN *str, DYNAMIC_STRING *json)
 | 
						|
{
 | 
						|
 | 
						|
  if (init_dynamic_string(json, NULL, str->length * 2, 100))
 | 
						|
    return ER_DYNCOL_RESOURCE;
 | 
						|
 | 
						|
  return mariadb_dyncol_json_internal(str, json, 1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Convert to DYNAMIC_COLUMN_VALUE values and names (LEX_STING) dynamic array
 | 
						|
 | 
						|
  @param str             The packed string
 | 
						|
  @param count           number of elements in the arrays
 | 
						|
  @param names           Where to put names (should be free by user)
 | 
						|
  @param vals            Where to put values (should be free by user)
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_unpack(DYNAMIC_COLUMN *str,
 | 
						|
                      uint *count,
 | 
						|
                      LEX_STRING **names, DYNAMIC_COLUMN_VALUE **vals)
 | 
						|
{
 | 
						|
  DYN_HEADER header;
 | 
						|
  char *nm;
 | 
						|
  uint i;
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
 | 
						|
  *count= 0; *names= 0; *vals= 0;
 | 
						|
 | 
						|
  if (str->length == 0)
 | 
						|
    return ER_DYNCOL_OK;                      /* no columns */
 | 
						|
 | 
						|
  if ((rc= init_read_hdr(&header, str)) < 0)
 | 
						|
    return rc;
 | 
						|
 | 
						|
 | 
						|
  if (header.entry_size * header.column_count + FIXED_HEADER_SIZE >
 | 
						|
      str->length)
 | 
						|
    return ER_DYNCOL_FORMAT;
 | 
						|
 | 
						|
  *vals= my_malloc(PSI_INSTRUMENT_ME,
 | 
						|
                   sizeof(DYNAMIC_COLUMN_VALUE)* header.column_count, MYF(0));
 | 
						|
  if (header.format == dyncol_fmt_num)
 | 
						|
  {
 | 
						|
    *names= my_malloc(PSI_INSTRUMENT_ME,
 | 
						|
                      sizeof(LEX_STRING) * header.column_count +
 | 
						|
                      DYNCOL_NUM_CHAR * header.column_count, MYF(0));
 | 
						|
    nm= (char *)((*names) + header.column_count);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    *names= my_malloc(PSI_INSTRUMENT_ME,
 | 
						|
                      sizeof(LEX_STRING) * header.column_count, MYF(0));
 | 
						|
    nm= 0;
 | 
						|
  }
 | 
						|
  if (!(*vals) || !(*names))
 | 
						|
  {
 | 
						|
    rc= ER_DYNCOL_RESOURCE;
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
 | 
						|
  for (i= 0, header.entry= header.header;
 | 
						|
       i < header.column_count;
 | 
						|
       i++, header.entry+= header.entry_size)
 | 
						|
  {
 | 
						|
    header.length=
 | 
						|
      hdr_interval_length(&header, header.entry + header.entry_size);
 | 
						|
    header.data= header.dtpool + header.offset;
 | 
						|
    /*
 | 
						|
      Check that the found data is within the ranges. This can happen if
 | 
						|
      we get data with wrong offsets.
 | 
						|
    */
 | 
						|
    if (header.length == DYNCOL_OFFSET_ERROR ||
 | 
						|
        header.length > INT_MAX || header.offset > header.data_size)
 | 
						|
    {
 | 
						|
      rc= ER_DYNCOL_FORMAT;
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
    if ((rc= dynamic_column_get_value(&header, (*vals) + i)) < 0)
 | 
						|
      goto err;
 | 
						|
 | 
						|
    if (header.format == dyncol_fmt_num)
 | 
						|
    {
 | 
						|
      uint num= uint2korr(header.entry);
 | 
						|
      (*names)[i].str= nm;
 | 
						|
      (*names)[i].length= snprintf(nm, DYNCOL_NUM_CHAR, "%u", num);
 | 
						|
      nm+= (*names)[i].length + 1;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if (read_name(&header, header.entry, (*names) + i))
 | 
						|
      {
 | 
						|
        rc= ER_DYNCOL_FORMAT;
 | 
						|
        goto err;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  *count= header.column_count;
 | 
						|
  return ER_DYNCOL_OK;
 | 
						|
 | 
						|
err:
 | 
						|
  if (*vals)
 | 
						|
  {
 | 
						|
    my_free(*vals);
 | 
						|
    *vals= 0;
 | 
						|
  }
 | 
						|
  if (*names)
 | 
						|
  {
 | 
						|
    my_free(*names);
 | 
						|
    *names= 0;
 | 
						|
  }
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Free arrays allocated by mariadb_dyncol_unpack()
 | 
						|
 | 
						|
  @param names           Where to put names (should be free by user)
 | 
						|
  @param vals            Where to put values (should be free by user)
 | 
						|
*/
 | 
						|
void mariadb_dyncol_unpack_free(LEX_STRING *names, DYNAMIC_COLUMN_VALUE *vals)
 | 
						|
{
 | 
						|
  my_free(names);
 | 
						|
  my_free(vals);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Get not NULL column count
 | 
						|
 | 
						|
  @param str             The packed string
 | 
						|
  @param column_count    Where to put column count
 | 
						|
 | 
						|
  @return ER_DYNCOL_* return code
 | 
						|
*/
 | 
						|
 | 
						|
enum enum_dyncol_func_result
 | 
						|
mariadb_dyncol_column_count(DYNAMIC_COLUMN *str, uint *column_count)
 | 
						|
{
 | 
						|
  DYN_HEADER header;
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
 | 
						|
  *(column_count)= 0;
 | 
						|
  if (str->length == 0)
 | 
						|
    return ER_DYNCOL_OK;
 | 
						|
 | 
						|
  if ((rc= init_read_hdr(&header, str)) < 0)
 | 
						|
    return rc;
 | 
						|
  *column_count= header.column_count;
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
/**
 | 
						|
 Free dynamic column
 | 
						|
 | 
						|
 @param str The packed string
 | 
						|
*/
 | 
						|
void mariadb_dyncol_free(DYNAMIC_COLUMN *str)
 | 
						|
{
 | 
						|
  dynstr_free(str);
 | 
						|
}
 |