mariadb/storage/innobase/api/api0api.cc
Sergei Golubchik e2e5d07b28 MDEV-6184 10.0.11 merge
InnoDB 5.6.16
2014-05-06 09:57:39 +02:00

4039 lines
98 KiB
C++

/*****************************************************************************
Copyright (c) 2008, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
*****************************************************************************/
/**************************************************//**
@file api/api0api.cc
InnoDB Native API
2008-08-01 Created Sunny Bains
3/20/2011 Jimmy Yang extracted from Embedded InnoDB
*******************************************************/
#include "univ.i"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "api0api.h"
#include "api0misc.h"
#include "srv0start.h"
#include "dict0dict.h"
#include "btr0pcur.h"
#include "row0ins.h"
#include "row0upd.h"
#include "row0vers.h"
#include "trx0roll.h"
#include "dict0crea.h"
#include "row0merge.h"
#include "pars0pars.h"
#include "lock0types.h"
#include "row0sel.h"
#include "lock0lock.h"
#include "rem0cmp.h"
#include "ut0dbg.h"
#include "dict0priv.h"
#include "ut0ut.h"
#include "ha_prototypes.h"
#include "trx0roll.h"
/** configure variable for binlog option with InnoDB APIs */
my_bool ib_binlog_enabled = FALSE;
/** configure variable for MDL option with InnoDB APIs */
my_bool ib_mdl_enabled = FALSE;
/** configure variable for disable rowlock with InnoDB APIs */
my_bool ib_disable_row_lock = FALSE;
/** configure variable for Transaction isolation levels */
ulong ib_trx_level_setting = IB_TRX_READ_UNCOMMITTED;
/** configure variable for background commit interval in seconds */
ulong ib_bk_commit_interval = 0;
/** InnoDB tuple types. */
enum ib_tuple_type_t{
TPL_TYPE_ROW, /*!< Data row tuple */
TPL_TYPE_KEY /*!< Index key tuple */
};
/** Query types supported. */
enum ib_qry_type_t{
QRY_NON, /*!< None/Sentinel */
QRY_INS, /*!< Insert operation */
QRY_UPD, /*!< Update operation */
QRY_SEL /*!< Select operation */
};
/** Query graph types. */
struct ib_qry_grph_t {
que_fork_t* ins; /*!< Innobase SQL query graph used
in inserts */
que_fork_t* upd; /*!< Innobase SQL query graph used
in updates or deletes */
que_fork_t* sel; /*!< dummy query graph used in
selects */
};
/** Query node types. */
struct ib_qry_node_t {
ins_node_t* ins; /*!< Innobase SQL insert node
used to perform inserts to the table */
upd_node_t* upd; /*!< Innobase SQL update node
used to perform updates and deletes */
sel_node_t* sel; /*!< Innobase SQL select node
used to perform selects on the table */
};
/** Query processing fields. */
struct ib_qry_proc_t {
ib_qry_node_t node; /*!< Query node*/
ib_qry_grph_t grph; /*!< Query graph */
};
/** Cursor instance for traversing tables/indexes. This will eventually
become row_prebuilt_t. */
struct ib_cursor_t {
mem_heap_t* heap; /*!< Instance heap */
mem_heap_t* query_heap; /*!< Heap to use for query graphs */
ib_qry_proc_t q_proc; /*!< Query processing info */
ib_match_mode_t match_mode; /*!< ib_cursor_moveto match mode */
row_prebuilt_t* prebuilt; /*!< For reading rows */
bool valid_trx; /*!< Valid transaction attached */
};
/** InnoDB table columns used during table and index schema creation. */
struct ib_col_t {
const char* name; /*!< Name of column */
ib_col_type_t ib_col_type; /*!< Main type of the column */
ulint len; /*!< Length of the column */
ib_col_attr_t ib_col_attr; /*!< Column attributes */
};
/** InnoDB index columns used during index and index schema creation. */
struct ib_key_col_t {
const char* name; /*!< Name of column */
ulint prefix_len; /*!< Column index prefix len or 0 */
};
struct ib_table_def_t;
/** InnoDB index schema used during index creation */
struct ib_index_def_t {
mem_heap_t* heap; /*!< Heap used to build this and all
its columns in the list */
const char* name; /*!< Index name */
dict_table_t* table; /*!< Parent InnoDB table */
ib_table_def_t* schema; /*!< Parent table schema that owns
this instance */
ibool clustered; /*!< True if clustered index */
ibool unique; /*!< True if unique index */
ib_vector_t* cols; /*!< Vector of columns */
trx_t* usr_trx; /*!< User transacton covering the
DDL operations */
};
/** InnoDB table schema used during table creation */
struct ib_table_def_t {
mem_heap_t* heap; /*!< Heap used to build this and all
its columns in the list */
const char* name; /*!< Table name */
ib_tbl_fmt_t ib_tbl_fmt; /*!< Row format */
ulint page_size; /*!< Page size */
ib_vector_t* cols; /*!< Vector of columns */
ib_vector_t* indexes; /*!< Vector of indexes */
dict_table_t* table; /* Table read from or NULL */
};
/** InnoDB tuple used for key operations. */
struct ib_tuple_t {
mem_heap_t* heap; /*!< Heap used to build
this and for copying
the column values. */
ib_tuple_type_t type; /*!< Tuple discriminitor. */
const dict_index_t* index; /*!< Index for tuple can be either
secondary or cluster index. */
dtuple_t* ptr; /*!< The internal tuple
instance */
};
/** The following counter is used to convey information to InnoDB
about server activity: in selects it is not sensible to call
srv_active_wake_master_thread after each fetch or search, we only do
it every INNOBASE_WAKE_INTERVAL'th step. */
#define INNOBASE_WAKE_INTERVAL 32
/*****************************************************************//**
Check whether the Innodb persistent cursor is positioned.
@return IB_TRUE if positioned */
UNIV_INLINE
ib_bool_t
ib_btr_cursor_is_positioned(
/*========================*/
btr_pcur_t* pcur) /*!< in: InnoDB persistent cursor */
{
return(pcur->old_stored == BTR_PCUR_OLD_STORED
&& (pcur->pos_state == BTR_PCUR_IS_POSITIONED
|| pcur->pos_state == BTR_PCUR_WAS_POSITIONED));
}
/********************************************************************//**
Open a table using the table id, if found then increment table ref count.
@return table instance if found */
static
dict_table_t*
ib_open_table_by_id(
/*================*/
ib_id_u64_t tid, /*!< in: table id to lookup */
ib_bool_t locked) /*!< in: TRUE if own dict mutex */
{
dict_table_t* table;
table_id_t table_id;
table_id = tid;
if (!locked) {
dict_mutex_enter_for_mysql();
}
table = dict_table_open_on_id(table_id, FALSE, DICT_TABLE_OP_NORMAL);
if (table != NULL && table->ibd_file_missing) {
table = NULL;
}
if (!locked) {
dict_mutex_exit_for_mysql();
}
return(table);
}
/********************************************************************//**
Open a table using the table name, if found then increment table ref count.
@return table instance if found */
UNIV_INTERN
void*
ib_open_table_by_name(
/*==================*/
const char* name) /*!< in: table name to lookup */
{
dict_table_t* table;
table = dict_table_open_on_name(name, FALSE, FALSE,
DICT_ERR_IGNORE_NONE);
if (table != NULL && table->ibd_file_missing) {
table = NULL;
}
return(table);
}
/********************************************************************//**
Find table using table name.
@return table instance if found */
static
dict_table_t*
ib_lookup_table_by_name(
/*====================*/
const char* name) /*!< in: table name to lookup */
{
dict_table_t* table;
table = dict_table_get_low(name);
if (table != NULL && table->ibd_file_missing) {
table = NULL;
}
return(table);
}
/********************************************************************//**
Increments innobase_active_counter and every INNOBASE_WAKE_INTERVALth
time calls srv_active_wake_master_thread. This function should be used
when a single database operation may introduce a small need for
server utility activity, like checkpointing. */
UNIV_INLINE
void
ib_wake_master_thread(void)
/*=======================*/
{
static ulint ib_signal_counter = 0;
++ib_signal_counter;
if ((ib_signal_counter % INNOBASE_WAKE_INTERVAL) == 0) {
srv_active_wake_master_thread();
}
}
/*********************************************************************//**
Calculate the max row size of the columns in a cluster index.
@return max row length */
UNIV_INLINE
ulint
ib_get_max_row_len(
/*===============*/
dict_index_t* cluster) /*!< in: cluster index */
{
ulint i;
ulint max_len = 0;
ulint n_fields = cluster->n_fields;
/* Add the size of the ordering columns in the
clustered index. */
for (i = 0; i < n_fields; ++i) {
const dict_col_t* col;
col = dict_index_get_nth_col(cluster, i);
/* Use the maximum output size of
mach_write_compressed(), although the encoded
length should always fit in 2 bytes. */
max_len += dict_col_get_max_size(col);
}
return(max_len);
}
/*****************************************************************//**
Read the columns from a rec into a tuple. */
static
void
ib_read_tuple(
/*==========*/
const rec_t* rec, /*!< in: Record to read */
ib_bool_t page_format, /*!< in: IB_TRUE if compressed format */
ib_tuple_t* tuple, /*!< in: tuple to read into */
void** rec_buf, /*!< in/out: row buffer */
ulint* len) /*!< in/out: buffer len */
{
ulint i;
void* ptr;
rec_t* copy;
ulint rec_meta_data;
ulint n_index_fields;
ulint offsets_[REC_OFFS_NORMAL_SIZE];
ulint* offsets = offsets_;
dtuple_t* dtuple = tuple->ptr;
const dict_index_t* index = tuple->index;
ulint offset_size;
rec_offs_init(offsets_);
offsets = rec_get_offsets(
rec, index, offsets, ULINT_UNDEFINED, &tuple->heap);
rec_meta_data = rec_get_info_bits(rec, page_format);
dtuple_set_info_bits(dtuple, rec_meta_data);
offset_size = rec_offs_size(offsets);
if (rec_buf && *rec_buf) {
if (*len < offset_size) {
free(*rec_buf);
*rec_buf = malloc(offset_size);
*len = offset_size;
}
ptr = *rec_buf;
} else {
/* Make a copy of the rec. */
ptr = mem_heap_alloc(tuple->heap, offset_size);
}
copy = rec_copy(ptr, rec, offsets);
n_index_fields = ut_min(
rec_offs_n_fields(offsets), dtuple_get_n_fields(dtuple));
for (i = 0; i < n_index_fields; ++i) {
ulint len;
const byte* data;
dfield_t* dfield;
if (tuple->type == TPL_TYPE_ROW) {
const dict_col_t* col;
ulint col_no;
const dict_field_t* index_field;
index_field = dict_index_get_nth_field(index, i);
col = dict_field_get_col(index_field);
col_no = dict_col_get_no(col);
dfield = dtuple_get_nth_field(dtuple, col_no);
} else {
dfield = dtuple_get_nth_field(dtuple, i);
}
data = rec_get_nth_field(copy, offsets, i, &len);
/* Fetch and copy any externally stored column. */
if (rec_offs_nth_extern(offsets, i)) {
ulint zip_size;
zip_size = dict_table_zip_size(index->table);
data = btr_rec_copy_externally_stored_field(
copy, offsets, zip_size, i, &len,
tuple->heap);
ut_a(len != UNIV_SQL_NULL);
}
dfield_set_data(dfield, data, len);
}
}
/*****************************************************************//**
Create an InnoDB key tuple.
@return tuple instance created, or NULL */
static
ib_tpl_t
ib_key_tuple_new_low(
/*=================*/
const dict_index_t* index, /*!< in: index for which tuple
required */
ulint n_cols, /*!< in: no. of user defined cols */
mem_heap_t* heap) /*!< in: memory heap */
{
ib_tuple_t* tuple;
ulint i;
ulint n_cmp_cols;
tuple = static_cast<ib_tuple_t*>(
mem_heap_alloc(heap, sizeof(*tuple)));
if (tuple == NULL) {
mem_heap_free(heap);
return(NULL);
}
tuple->heap = heap;
tuple->index = index;
tuple->type = TPL_TYPE_KEY;
/* Is it a generated clustered index ? */
if (n_cols == 0) {
++n_cols;
}
tuple->ptr = dtuple_create(heap, n_cols);
/* Copy types and set to SQL_NULL. */
dict_index_copy_types(tuple->ptr, index, n_cols);
for (i = 0; i < n_cols; i++) {
dfield_t* dfield;
dfield = dtuple_get_nth_field(tuple->ptr, i);
dfield_set_null(dfield);
}
n_cmp_cols = dict_index_get_n_ordering_defined_by_user(index);
dtuple_set_n_fields_cmp(tuple->ptr, n_cmp_cols);
return((ib_tpl_t) tuple);
}
/*****************************************************************//**
Create an InnoDB key tuple.
@return tuple instance created, or NULL */
static
ib_tpl_t
ib_key_tuple_new(
/*=============*/
const dict_index_t* index, /*!< in: index of tuple */
ulint n_cols) /*!< in: no. of user defined cols */
{
mem_heap_t* heap;
heap = mem_heap_create(64);
if (heap == NULL) {
return(NULL);
}
return(ib_key_tuple_new_low(index, n_cols, heap));
}
/*****************************************************************//**
Create an InnoDB row tuple.
@return tuple instance, or NULL */
static
ib_tpl_t
ib_row_tuple_new_low(
/*=================*/
const dict_index_t* index, /*!< in: index of tuple */
ulint n_cols, /*!< in: no. of cols in tuple */
mem_heap_t* heap) /*!< in: memory heap */
{
ib_tuple_t* tuple;
tuple = static_cast<ib_tuple_t*>(mem_heap_alloc(heap, sizeof(*tuple)));
if (tuple == NULL) {
mem_heap_free(heap);
return(NULL);
}
tuple->heap = heap;
tuple->index = index;
tuple->type = TPL_TYPE_ROW;
tuple->ptr = dtuple_create(heap, n_cols);
/* Copy types and set to SQL_NULL. */
dict_table_copy_types(tuple->ptr, index->table);
return((ib_tpl_t) tuple);
}
/*****************************************************************//**
Create an InnoDB row tuple.
@return tuple instance, or NULL */
static
ib_tpl_t
ib_row_tuple_new(
/*=============*/
const dict_index_t* index, /*!< in: index of tuple */
ulint n_cols) /*!< in: no. of cols in tuple */
{
mem_heap_t* heap;
heap = mem_heap_create(64);
if (heap == NULL) {
return(NULL);
}
return(ib_row_tuple_new_low(index, n_cols, heap));
}
/*****************************************************************//**
Begin a transaction.
@return innobase txn handle */
UNIV_INTERN
ib_err_t
ib_trx_start(
/*=========*/
ib_trx_t ib_trx, /*!< in: transaction to restart */
ib_trx_level_t ib_trx_level, /*!< in: trx isolation level */
ib_bool_t read_write, /*!< in: true if read write
transaction */
ib_bool_t auto_commit, /*!< in: auto commit after each
single DML */
void* thd) /*!< in: THD */
{
ib_err_t err = DB_SUCCESS;
trx_t* trx = (trx_t*) ib_trx;
ut_a(ib_trx_level <= IB_TRX_SERIALIZABLE);
trx->api_trx = true;
trx->api_auto_commit = auto_commit;
trx->read_write = read_write;
trx_start_if_not_started(trx);
trx->isolation_level = ib_trx_level;
/* FIXME: This is a place holder, we should add an arg that comes
from the client. */
trx->mysql_thd = static_cast<THD*>(thd);
return(err);
}
/*****************************************************************//**
Begin a transaction. This will allocate a new transaction handle.
put the transaction in the active state.
@return innobase txn handle */
UNIV_INTERN
ib_trx_t
ib_trx_begin(
/*=========*/
ib_trx_level_t ib_trx_level, /*!< in: trx isolation level */
ib_bool_t read_write, /*!< in: true if read write
transaction */
ib_bool_t auto_commit) /*!< in: auto commit after each
single DML */
{
trx_t* trx;
ib_bool_t started;
trx = trx_allocate_for_mysql();
started = ib_trx_start(static_cast<ib_trx_t>(trx), ib_trx_level,
read_write, auto_commit, NULL);
ut_a(started);
return(static_cast<ib_trx_t>(trx));
}
/*****************************************************************//**
Get the transaction's state.
@return transaction state */
UNIV_INTERN
ib_trx_state_t
ib_trx_state(
/*=========*/
ib_trx_t ib_trx) /*!< in: trx handle */
{
trx_t* trx = (trx_t*) ib_trx;
return((ib_trx_state_t) trx->state);
}
/*****************************************************************//**
Get a trx start time.
@return trx start_time */
UNIV_INTERN
ib_u64_t
ib_trx_get_start_time(
/*==================*/
ib_trx_t ib_trx) /*!< in: transaction */
{
trx_t* trx = (trx_t*) ib_trx;
return(static_cast<ib_u64_t>(trx->start_time));
}
/*****************************************************************//**
Release the resources of the transaction.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_trx_release(
/*===========*/
ib_trx_t ib_trx) /*!< in: trx handle */
{
trx_t* trx = (trx_t*) ib_trx;
ut_ad(trx != NULL);
trx_free_for_mysql(trx);
return(DB_SUCCESS);
}
/*****************************************************************//**
Commit a transaction. This function will also release the schema
latches too.
@return DB_SUCCESS or err code */
ib_err_t
ib_trx_commit(
/*==========*/
ib_trx_t ib_trx) /*!< in: trx handle */
{
ib_err_t err = DB_SUCCESS;
trx_t* trx = (trx_t*) ib_trx;
if (trx->state == TRX_STATE_NOT_STARTED) {
return(err);
}
trx_commit(trx);
return(DB_SUCCESS);
}
/*****************************************************************//**
Rollback a transaction. This function will also release the schema
latches too.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_trx_rollback(
/*============*/
ib_trx_t ib_trx) /*!< in: trx handle */
{
ib_err_t err;
trx_t* trx = (trx_t*) ib_trx;
err = static_cast<ib_err_t>(trx_rollback_for_mysql(trx));
/* It should always succeed */
ut_a(err == DB_SUCCESS);
ib_wake_master_thread();
return(err);
}
/*****************************************************************//**
Find an index definition from the index vector using index name.
@return index def. if found else NULL */
UNIV_INLINE
const ib_index_def_t*
ib_table_find_index(
/*================*/
ib_vector_t* indexes, /*!< in: vector of indexes */
const char* name) /*!< in: index name */
{
ulint i;
for (i = 0; i < ib_vector_size(indexes); ++i) {
const ib_index_def_t* index_def;
index_def = (ib_index_def_t*) ib_vector_get(indexes, i);
if (innobase_strcasecmp(name, index_def->name) == 0) {
return(index_def);
}
}
return(NULL);
}
/*****************************************************************//**
Get the InnoDB internal precise type from the schema column definition.
@return precise type in api format */
UNIV_INLINE
ulint
ib_col_get_prtype(
/*==============*/
const ib_col_t* ib_col) /*!< in: column definition */
{
ulint prtype = 0;
if (ib_col->ib_col_attr & IB_COL_UNSIGNED) {
prtype |= DATA_UNSIGNED;
ut_a(ib_col->ib_col_type == IB_INT);
}
if (ib_col->ib_col_attr & IB_COL_NOT_NULL) {
prtype |= DATA_NOT_NULL;
}
return(prtype);
}
/*****************************************************************//**
Get the InnoDB internal main type from the schema column definition.
@return column main type */
UNIV_INLINE
ulint
ib_col_get_mtype(
/*==============*/
const ib_col_t* ib_col) /*!< in: column definition */
{
/* Note: The api0api.h types should map directly to
the internal numeric codes. */
return(ib_col->ib_col_type);
}
/*****************************************************************//**
Find a column in the the column vector with the same name.
@return col. def. if found else NULL */
UNIV_INLINE
const ib_col_t*
ib_table_find_col(
/*==============*/
const ib_vector_t* cols, /*!< in: column list head */
const char* name) /*!< in: column name to find */
{
ulint i;
for (i = 0; i < ib_vector_size(cols); ++i) {
const ib_col_t* ib_col;
ib_col = static_cast<const ib_col_t*>(
ib_vector_get((ib_vector_t*) cols, i));
if (innobase_strcasecmp(ib_col->name, name) == 0) {
return(ib_col);
}
}
return(NULL);
}
/*****************************************************************//**
Find a column in the the column list with the same name.
@return col. def. if found else NULL */
UNIV_INLINE
const ib_key_col_t*
ib_index_find_col(
/*==============*/
ib_vector_t* cols, /*!< in: column list head */
const char* name) /*!< in: column name to find */
{
ulint i;
for (i = 0; i < ib_vector_size(cols); ++i) {
const ib_key_col_t* ib_col;
ib_col = static_cast<ib_key_col_t*>(ib_vector_get(cols, i));
if (innobase_strcasecmp(ib_col->name, name) == 0) {
return(ib_col);
}
}
return(NULL);
}
#ifdef __WIN__
/*****************************************************************//**
Convert a string to lower case. */
static
void
ib_to_lower_case(
/*=============*/
char* ptr) /*!< string to convert to lower case */
{
while (*ptr) {
*ptr = tolower(*ptr);
++ptr;
}
}
#endif /* __WIN__ */
/*****************************************************************//**
Normalizes a table name string. A normalized name consists of the
database name catenated to '/' and table name. An example:
test/mytable. On Windows normalization puts both the database name and the
table name always to lower case. This function can be called for system
tables and they don't have a database component. For tables that don't have
a database component, we don't normalize them to lower case on Windows.
The assumption is that they are system tables that reside in the system
table space. */
static
void
ib_normalize_table_name(
/*====================*/
char* norm_name, /*!< out: normalized name as a
null-terminated string */
const char* name) /*!< in: table name string */
{
const char* ptr = name;
/* Scan name from the end */
ptr += ut_strlen(name) - 1;
/* Find the start of the table name. */
while (ptr >= name && *ptr != '\\' && *ptr != '/' && ptr > name) {
--ptr;
}
/* For system tables there is no '/' or dbname. */
ut_a(ptr >= name);
if (ptr > name) {
const char* db_name;
const char* table_name;
table_name = ptr + 1;
--ptr;
while (ptr >= name && *ptr != '\\' && *ptr != '/') {
ptr--;
}
db_name = ptr + 1;
memcpy(norm_name, db_name,
ut_strlen(name) + 1 - (db_name - name));
norm_name[table_name - db_name - 1] = '/';
#ifdef __WIN__
ib_to_lower_case(norm_name);
#endif
} else {
ut_strcpy(norm_name, name);
}
}
/*****************************************************************//**
Check whether the table name conforms to our requirements. Currently
we only do a simple check for the presence of a '/'.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_table_name_check(
/*================*/
const char* name) /*!< in: table name to check */
{
const char* slash = NULL;
ulint len = ut_strlen(name);
if (len < 2
|| *name == '/'
|| name[len - 1] == '/'
|| (name[0] == '.' && name[1] == '/')
|| (name[0] == '.' && name[1] == '.' && name[2] == '/')) {
return(DB_DATA_MISMATCH);
}
for ( ; *name; ++name) {
#ifdef __WIN__
/* Check for reserved characters in DOS filenames. */
switch (*name) {
case ':':
case '|':
case '"':
case '*':
case '<':
case '>':
return(DB_DATA_MISMATCH);
}
#endif /* __WIN__ */
if (*name == '/') {
if (slash) {
return(DB_DATA_MISMATCH);
}
slash = name;
}
}
return(slash ? DB_SUCCESS : DB_DATA_MISMATCH);
}
/*****************************************************************//**
Get an index definition that is tagged as a clustered index.
@return cluster index schema */
UNIV_INLINE
ib_index_def_t*
ib_find_clustered_index(
/*====================*/
ib_vector_t* indexes) /*!< in: index defs. to search */
{
ulint i;
ulint n_indexes;
n_indexes = ib_vector_size(indexes);
for (i = 0; i < n_indexes; ++i) {
ib_index_def_t* ib_index_def;
ib_index_def = static_cast<ib_index_def_t*>(
ib_vector_get(indexes, i));
if (ib_index_def->clustered) {
return(ib_index_def);
}
}
return(NULL);
}
/*****************************************************************//**
Get a table id. The caller must have acquired the dictionary mutex.
@return DB_SUCCESS if found */
static
ib_err_t
ib_table_get_id_low(
/*================*/
const char* table_name, /*!< in: table to find */
ib_id_u64_t* table_id) /*!< out: table id if found */
{
dict_table_t* table;
ib_err_t err = DB_TABLE_NOT_FOUND;
*table_id = 0;
table = ib_lookup_table_by_name(table_name);
if (table != NULL) {
*table_id = (table->id);
err = DB_SUCCESS;
}
return(err);
}
/*****************************************************************//**
Create an internal cursor instance.
@return DB_SUCCESS or err code */
static
ib_err_t
ib_create_cursor(
/*=============*/
ib_crsr_t* ib_crsr, /*!< out: InnoDB cursor */
dict_table_t* table, /*!< in: table instance */
dict_index_t* index, /*!< in: index to use */
trx_t* trx) /*!< in: transaction */
{
mem_heap_t* heap;
ib_cursor_t* cursor;
ib_err_t err = DB_SUCCESS;
heap = mem_heap_create(sizeof(*cursor) * 2);
if (heap != NULL) {
row_prebuilt_t* prebuilt;
cursor = static_cast<ib_cursor_t*>(
mem_heap_zalloc(heap, sizeof(*cursor)));
cursor->heap = heap;
cursor->query_heap = mem_heap_create(64);
if (cursor->query_heap == NULL) {
mem_heap_free(heap);
return(DB_OUT_OF_MEMORY);
}
cursor->prebuilt = row_create_prebuilt(table, 0);
prebuilt = cursor->prebuilt;
prebuilt->trx = trx;
cursor->valid_trx = TRUE;
prebuilt->table = table;
prebuilt->select_lock_type = LOCK_NONE;
prebuilt->innodb_api = TRUE;
prebuilt->index = index;
ut_a(prebuilt->index != NULL);
if (prebuilt->trx != NULL) {
++prebuilt->trx->n_mysql_tables_in_use;
prebuilt->index_usable =
row_merge_is_index_usable(
prebuilt->trx, prebuilt->index);
/* Assign a read view if the transaction does
not have it yet */
trx_assign_read_view(prebuilt->trx);
}
*ib_crsr = (ib_crsr_t) cursor;
} else {
err = DB_OUT_OF_MEMORY;
}
return(err);
}
/*****************************************************************//**
Create an internal cursor instance, and set prebuilt->index to index
with supplied index_id.
@return DB_SUCCESS or err code */
static
ib_err_t
ib_create_cursor_with_index_id(
/*===========================*/
ib_crsr_t* ib_crsr, /*!< out: InnoDB cursor */
dict_table_t* table, /*!< in: table instance */
ib_id_u64_t index_id, /*!< in: index id or 0 */
trx_t* trx) /*!< in: transaction */
{
dict_index_t* index;
if (index_id != 0) {
mutex_enter(&dict_sys->mutex);
index = dict_index_find_on_id_low(index_id);
mutex_exit(&dict_sys->mutex);
} else {
index = dict_table_get_first_index(table);
}
return(ib_create_cursor(ib_crsr, table, index, trx));
}
/*****************************************************************//**
Open an InnoDB table and return a cursor handle to it.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_open_table_using_id(
/*==========================*/
ib_id_u64_t table_id, /*!< in: table id of table to open */
ib_trx_t ib_trx, /*!< in: Current transaction handle
can be NULL */
ib_crsr_t* ib_crsr) /*!< out,own: InnoDB cursor */
{
ib_err_t err;
dict_table_t* table;
if (ib_trx == NULL || !ib_schema_lock_is_exclusive(ib_trx)) {
table = ib_open_table_by_id(table_id, FALSE);
} else {
table = ib_open_table_by_id(table_id, TRUE);
}
if (table == NULL) {
return(DB_TABLE_NOT_FOUND);
}
err = ib_create_cursor_with_index_id(ib_crsr, table, 0,
(trx_t*) ib_trx);
return(err);
}
/*****************************************************************//**
Open an InnoDB index and return a cursor handle to it.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_open_index_using_id(
/*==========================*/
ib_id_u64_t index_id, /*!< in: index id of index to open */
ib_trx_t ib_trx, /*!< in: Current transaction handle
can be NULL */
ib_crsr_t* ib_crsr) /*!< out: InnoDB cursor */
{
ib_err_t err;
dict_table_t* table;
ulint table_id = (ulint)( index_id >> 32);
if (ib_trx == NULL || !ib_schema_lock_is_exclusive(ib_trx)) {
table = ib_open_table_by_id(table_id, FALSE);
} else {
table = ib_open_table_by_id(table_id, TRUE);
}
if (table == NULL) {
return(DB_TABLE_NOT_FOUND);
}
/* We only return the lower 32 bits of the dulint. */
err = ib_create_cursor_with_index_id(
ib_crsr, table, index_id, (trx_t*) ib_trx);
if (ib_crsr != NULL) {
const ib_cursor_t* cursor;
cursor = *(ib_cursor_t**) ib_crsr;
if (cursor->prebuilt->index == NULL) {
ib_err_t crsr_err;
crsr_err = ib_cursor_close(*ib_crsr);
ut_a(crsr_err == DB_SUCCESS);
*ib_crsr = NULL;
}
}
return(err);
}
/*****************************************************************//**
Open an InnoDB secondary index cursor and return a cursor handle to it.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_open_index_using_name(
/*============================*/
ib_crsr_t ib_open_crsr, /*!< in: open/active cursor */
const char* index_name, /*!< in: secondary index name */
ib_crsr_t* ib_crsr, /*!< out,own: InnoDB index cursor */
int* idx_type, /*!< out: index is cluster index */
ib_id_u64_t* idx_id) /*!< out: index id */
{
dict_table_t* table;
dict_index_t* index;
index_id_t index_id = 0;
ib_err_t err = DB_TABLE_NOT_FOUND;
ib_cursor_t* cursor = (ib_cursor_t*) ib_open_crsr;
*idx_type = 0;
*idx_id = 0;
*ib_crsr = NULL;
/* We want to increment the ref count, so we do a redundant search. */
table = dict_table_open_on_id(cursor->prebuilt->table->id,
FALSE, DICT_TABLE_OP_NORMAL);
ut_a(table != NULL);
/* The first index is always the cluster index. */
index = dict_table_get_first_index(table);
/* Traverse the user defined indexes. */
while (index != NULL) {
if (innobase_strcasecmp(index->name, index_name) == 0) {
index_id = index->id;
*idx_type = index->type;
*idx_id = index_id;
break;
}
index = UT_LIST_GET_NEXT(indexes, index);
}
if (!index_id) {
dict_table_close(table, FALSE, FALSE);
return(DB_ERROR);
}
if (index_id > 0) {
ut_ad(index->id == index_id);
err = ib_create_cursor(
ib_crsr, table, index, cursor->prebuilt->trx);
}
if (*ib_crsr != NULL) {
const ib_cursor_t* cursor;
cursor = *(ib_cursor_t**) ib_crsr;
if (cursor->prebuilt->index == NULL) {
err = ib_cursor_close(*ib_crsr);
ut_a(err == DB_SUCCESS);
*ib_crsr = NULL;
}
}
return(err);
}
/*****************************************************************//**
Open an InnoDB table and return a cursor handle to it.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_open_table(
/*=================*/
const char* name, /*!< in: table name */
ib_trx_t ib_trx, /*!< in: Current transaction handle
can be NULL */
ib_crsr_t* ib_crsr) /*!< out,own: InnoDB cursor */
{
ib_err_t err;
dict_table_t* table;
char* normalized_name;
normalized_name = static_cast<char*>(mem_alloc(ut_strlen(name) + 1));
ib_normalize_table_name(normalized_name, name);
if (ib_trx != NULL) {
if (!ib_schema_lock_is_exclusive(ib_trx)) {
table = (dict_table_t*)ib_open_table_by_name(
normalized_name);
} else {
/* NOTE: We do not acquire MySQL metadata lock */
table = ib_lookup_table_by_name(normalized_name);
}
} else {
table = (dict_table_t*)ib_open_table_by_name(normalized_name);
}
mem_free(normalized_name);
normalized_name = NULL;
/* It can happen that another thread has created the table but
not the cluster index or it's a broken table definition. Refuse to
open if that's the case. */
if (table != NULL && dict_table_get_first_index(table) == NULL) {
table = NULL;
}
if (table != NULL) {
err = ib_create_cursor_with_index_id(ib_crsr, table, 0,
(trx_t*) ib_trx);
} else {
err = DB_TABLE_NOT_FOUND;
}
return(err);
}
/********************************************************************//**
Free a context struct for a table handle. */
static
void
ib_qry_proc_free(
/*=============*/
ib_qry_proc_t* q_proc) /*!< in, own: qproc struct */
{
que_graph_free_recursive(q_proc->grph.ins);
que_graph_free_recursive(q_proc->grph.upd);
que_graph_free_recursive(q_proc->grph.sel);
memset(q_proc, 0x0, sizeof(*q_proc));
}
/*****************************************************************//**
set a cursor trx to NULL */
UNIV_INTERN
void
ib_cursor_clear_trx(
/*================*/
ib_crsr_t ib_crsr) /*!< in/out: InnoDB cursor */
{
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
cursor->prebuilt->trx = NULL;
}
/*****************************************************************//**
Reset the cursor.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_reset(
/*============*/
ib_crsr_t ib_crsr) /*!< in/out: InnoDB cursor */
{
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
row_prebuilt_t* prebuilt = cursor->prebuilt;
if (cursor->valid_trx && prebuilt->trx != NULL
&& prebuilt->trx->n_mysql_tables_in_use > 0) {
--prebuilt->trx->n_mysql_tables_in_use;
}
/* The fields in this data structure are allocated from
the query heap and so need to be reset too. */
ib_qry_proc_free(&cursor->q_proc);
mem_heap_empty(cursor->query_heap);
return(DB_SUCCESS);
}
/*****************************************************************//**
update the cursor with new transactions and also reset the cursor
@return DB_SUCCESS or err code */
ib_err_t
ib_cursor_new_trx(
/*==============*/
ib_crsr_t ib_crsr, /*!< in/out: InnoDB cursor */
ib_trx_t ib_trx) /*!< in: transaction */
{
ib_err_t err = DB_SUCCESS;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
trx_t* trx = (trx_t*) ib_trx;
row_prebuilt_t* prebuilt = cursor->prebuilt;
row_update_prebuilt_trx(prebuilt, trx);
cursor->valid_trx = TRUE;
trx_assign_read_view(prebuilt->trx);
ib_qry_proc_free(&cursor->q_proc);
mem_heap_empty(cursor->query_heap);
return(err);
}
/*****************************************************************//**
Commit the transaction in a cursor
@return DB_SUCCESS or err code */
ib_err_t
ib_cursor_commit_trx(
/*=================*/
ib_crsr_t ib_crsr, /*!< in/out: InnoDB cursor */
ib_trx_t ib_trx) /*!< in: transaction */
{
ib_err_t err = DB_SUCCESS;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
#ifdef UNIV_DEBUG
row_prebuilt_t* prebuilt = cursor->prebuilt;
ut_ad(prebuilt->trx == (trx_t*) ib_trx);
#endif /* UNIV_DEBUG */
ib_trx_commit(ib_trx);
cursor->valid_trx = FALSE;
return(err);
}
/*****************************************************************//**
Close an InnoDB table and free the cursor.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_close(
/*============*/
ib_crsr_t ib_crsr) /*!< in,own: InnoDB cursor */
{
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
row_prebuilt_t* prebuilt;
trx_t* trx;
if (!cursor) {
return(DB_SUCCESS);
}
prebuilt = cursor->prebuilt;
trx = prebuilt->trx;
ib_qry_proc_free(&cursor->q_proc);
/* The transaction could have been detached from the cursor. */
if (cursor->valid_trx && trx != NULL
&& trx->n_mysql_tables_in_use > 0) {
--trx->n_mysql_tables_in_use;
}
row_prebuilt_free(prebuilt, FALSE);
cursor->prebuilt = NULL;
mem_heap_free(cursor->query_heap);
mem_heap_free(cursor->heap);
cursor = NULL;
return(DB_SUCCESS);
}
/*****************************************************************//**
Close the table, decrement n_ref_count count.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_close_table(
/*==================*/
ib_crsr_t ib_crsr) /*!< in,own: InnoDB cursor */
{
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
row_prebuilt_t* prebuilt = cursor->prebuilt;
if (prebuilt && prebuilt->table) {
dict_table_close(prebuilt->table, FALSE, FALSE);
}
return(DB_SUCCESS);
}
/**********************************************************************//**
Run the insert query and do error handling.
@return DB_SUCCESS or error code */
UNIV_INLINE
ib_err_t
ib_insert_row_with_lock_retry(
/*==========================*/
que_thr_t* thr, /*!< in: insert query graph */
ins_node_t* node, /*!< in: insert node for the query */
trx_savept_t* savept) /*!< in: savepoint to rollback to
in case of an error */
{
trx_t* trx;
ib_err_t err;
ib_bool_t lock_wait;
trx = thr_get_trx(thr);
do {
thr->run_node = node;
thr->prev_node = node;
row_ins_step(thr);
err = trx->error_state;
if (err != DB_SUCCESS) {
que_thr_stop_for_mysql(thr);
thr->lock_state = QUE_THR_LOCK_ROW;
lock_wait = ib_handle_errors(&err, trx, thr, savept);
thr->lock_state = QUE_THR_LOCK_NOLOCK;
} else {
lock_wait = FALSE;
}
} while (lock_wait);
return(err);
}
/*****************************************************************//**
Write a row.
@return DB_SUCCESS or err code */
static
ib_err_t
ib_execute_insert_query_graph(
/*==========================*/
dict_table_t* table, /*!< in: table where to insert */
que_fork_t* ins_graph, /*!< in: query graph */
ins_node_t* node) /*!< in: insert node */
{
trx_t* trx;
que_thr_t* thr;
trx_savept_t savept;
ib_err_t err = DB_SUCCESS;
trx = ins_graph->trx;
savept = trx_savept_take(trx);
thr = que_fork_get_first_thr(ins_graph);
que_thr_move_to_run_state_for_mysql(thr, trx);
err = ib_insert_row_with_lock_retry(thr, node, &savept);
if (err == DB_SUCCESS) {
que_thr_stop_for_mysql_no_error(thr, trx);
dict_table_n_rows_inc(table);
srv_stats.n_rows_inserted.inc();
}
trx->op_info = "";
return(err);
}
/*****************************************************************//**
Create an insert query graph node. */
static
void
ib_insert_query_graph_create(
/*==========================*/
ib_cursor_t* cursor) /*!< in: Cursor instance */
{
ib_qry_proc_t* q_proc = &cursor->q_proc;
ib_qry_node_t* node = &q_proc->node;
trx_t* trx = cursor->prebuilt->trx;
ut_a(trx->state != TRX_STATE_NOT_STARTED);
if (node->ins == NULL) {
dtuple_t* row;
ib_qry_grph_t* grph = &q_proc->grph;
mem_heap_t* heap = cursor->query_heap;
dict_table_t* table = cursor->prebuilt->table;
node->ins = ins_node_create(INS_DIRECT, table, heap);
node->ins->select = NULL;
node->ins->values_list = NULL;
row = dtuple_create(heap, dict_table_get_n_cols(table));
dict_table_copy_types(row, table);
ins_node_set_new_row(node->ins, row);
grph->ins = static_cast<que_fork_t*>(
que_node_get_parent(
pars_complete_graph_for_exec(node->ins, trx,
heap)));
grph->ins->state = QUE_FORK_ACTIVE;
}
}
/*****************************************************************//**
Insert a row to a table.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_insert_row(
/*=================*/
ib_crsr_t ib_crsr, /*!< in/out: InnoDB cursor instance */
const ib_tpl_t ib_tpl) /*!< in: tuple to insert */
{
ib_ulint_t i;
ib_qry_node_t* node;
ib_qry_proc_t* q_proc;
ulint n_fields;
dtuple_t* dst_dtuple;
ib_err_t err = DB_SUCCESS;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
const ib_tuple_t* src_tuple = (const ib_tuple_t*) ib_tpl;
ib_insert_query_graph_create(cursor);
ut_ad(src_tuple->type == TPL_TYPE_ROW);
q_proc = &cursor->q_proc;
node = &q_proc->node;
node->ins->state = INS_NODE_ALLOC_ROW_ID;
dst_dtuple = node->ins->row;
n_fields = dtuple_get_n_fields(src_tuple->ptr);
ut_ad(n_fields == dtuple_get_n_fields(dst_dtuple));
/* Do a shallow copy of the data fields and check for NULL
constraints on columns. */
for (i = 0; i < n_fields; i++) {
ulint mtype;
dfield_t* src_field;
dfield_t* dst_field;
src_field = dtuple_get_nth_field(src_tuple->ptr, i);
mtype = dtype_get_mtype(dfield_get_type(src_field));
/* Don't touch the system columns. */
if (mtype != DATA_SYS) {
ulint prtype;
prtype = dtype_get_prtype(dfield_get_type(src_field));
if ((prtype & DATA_NOT_NULL)
&& dfield_is_null(src_field)) {
err = DB_DATA_MISMATCH;
break;
}
dst_field = dtuple_get_nth_field(dst_dtuple, i);
ut_ad(mtype
== dtype_get_mtype(dfield_get_type(dst_field)));
/* Do a shallow copy. */
dfield_set_data(
dst_field, src_field->data, src_field->len);
if (dst_field->len != IB_SQL_NULL) {
UNIV_MEM_ASSERT_RW(dst_field->data,
dst_field->len);
}
}
}
if (err == DB_SUCCESS) {
err = ib_execute_insert_query_graph(
src_tuple->index->table, q_proc->grph.ins, node->ins);
}
srv_active_wake_master_thread();
return(err);
}
/*********************************************************************//**
Gets pointer to a prebuilt update vector used in updates.
@return update vector */
UNIV_INLINE
upd_t*
ib_update_vector_create(
/*====================*/
ib_cursor_t* cursor) /*!< in: current cursor */
{
trx_t* trx = cursor->prebuilt->trx;
mem_heap_t* heap = cursor->query_heap;
dict_table_t* table = cursor->prebuilt->table;
ib_qry_proc_t* q_proc = &cursor->q_proc;
ib_qry_grph_t* grph = &q_proc->grph;
ib_qry_node_t* node = &q_proc->node;
ut_a(trx->state != TRX_STATE_NOT_STARTED);
if (node->upd == NULL) {
node->upd = static_cast<upd_node_t*>(
row_create_update_node_for_mysql(table, heap));
}
grph->upd = static_cast<que_fork_t*>(
que_node_get_parent(
pars_complete_graph_for_exec(node->upd, trx, heap)));
grph->upd->state = QUE_FORK_ACTIVE;
return(node->upd->update);
}
/**********************************************************************//**
Note that a column has changed. */
static
void
ib_update_col(
/*==========*/
ib_cursor_t* cursor, /*!< in: current cursor */
upd_field_t* upd_field, /*!< in/out: update field */
ulint col_no, /*!< in: column number */
dfield_t* dfield) /*!< in: updated dfield */
{
ulint data_len;
dict_table_t* table = cursor->prebuilt->table;
dict_index_t* index = dict_table_get_first_index(table);
data_len = dfield_get_len(dfield);
if (data_len == UNIV_SQL_NULL) {
dfield_set_null(&upd_field->new_val);
} else {
dfield_copy_data(&upd_field->new_val, dfield);
}
upd_field->exp = NULL;
upd_field->orig_len = 0;
upd_field->field_no = dict_col_get_clust_pos(
&table->cols[col_no], index);
}
/**********************************************************************//**
Checks which fields have changed in a row and stores the new data
to an update vector.
@return DB_SUCCESS or err code */
static
ib_err_t
ib_calc_diff(
/*=========*/
ib_cursor_t* cursor, /*!< in: current cursor */
upd_t* upd, /*!< in/out: update vector */
const ib_tuple_t*old_tuple, /*!< in: Old tuple in table */
const ib_tuple_t*new_tuple) /*!< in: New tuple to update */
{
ulint i;
ulint n_changed = 0;
ib_err_t err = DB_SUCCESS;
ulint n_fields = dtuple_get_n_fields(new_tuple->ptr);
ut_a(old_tuple->type == TPL_TYPE_ROW);
ut_a(new_tuple->type == TPL_TYPE_ROW);
ut_a(old_tuple->index->table == new_tuple->index->table);
for (i = 0; i < n_fields; ++i) {
ulint mtype;
ulint prtype;
upd_field_t* upd_field;
dfield_t* new_dfield;
dfield_t* old_dfield;
new_dfield = dtuple_get_nth_field(new_tuple->ptr, i);
old_dfield = dtuple_get_nth_field(old_tuple->ptr, i);
mtype = dtype_get_mtype(dfield_get_type(old_dfield));
prtype = dtype_get_prtype(dfield_get_type(old_dfield));
/* Skip the system columns */
if (mtype == DATA_SYS) {
continue;
} else if ((prtype & DATA_NOT_NULL)
&& dfield_is_null(new_dfield)) {
err = DB_DATA_MISMATCH;
break;
}
if (dfield_get_len(new_dfield) != dfield_get_len(old_dfield)
|| (!dfield_is_null(old_dfield)
&& memcmp(dfield_get_data(new_dfield),
dfield_get_data(old_dfield),
dfield_get_len(old_dfield)) != 0)) {
upd_field = &upd->fields[n_changed];
ib_update_col(cursor, upd_field, i, new_dfield);
++n_changed;
}
}
if (err == DB_SUCCESS) {
upd->info_bits = 0;
upd->n_fields = n_changed;
}
return(err);
}
/**********************************************************************//**
Run the update query and do error handling.
@return DB_SUCCESS or error code */
UNIV_INLINE
ib_err_t
ib_update_row_with_lock_retry(
/*==========================*/
que_thr_t* thr, /*!< in: Update query graph */
upd_node_t* node, /*!< in: Update node for the query */
trx_savept_t* savept) /*!< in: savepoint to rollback to
in case of an error */
{
trx_t* trx;
ib_err_t err;
ib_bool_t lock_wait;
trx = thr_get_trx(thr);
do {
thr->run_node = node;
thr->prev_node = node;
row_upd_step(thr);
err = trx->error_state;
if (err != DB_SUCCESS) {
que_thr_stop_for_mysql(thr);
if (err != DB_RECORD_NOT_FOUND) {
thr->lock_state = QUE_THR_LOCK_ROW;
lock_wait = ib_handle_errors(
&err, trx, thr, savept);
thr->lock_state = QUE_THR_LOCK_NOLOCK;
} else {
lock_wait = FALSE;
}
} else {
lock_wait = FALSE;
}
} while (lock_wait);
return(err);
}
/*********************************************************************//**
Does an update or delete of a row.
@return DB_SUCCESS or err code */
UNIV_INLINE
ib_err_t
ib_execute_update_query_graph(
/*==========================*/
ib_cursor_t* cursor, /*!< in: Cursor instance */
btr_pcur_t* pcur) /*!< in: Btree persistent cursor */
{
ib_err_t err;
que_thr_t* thr;
upd_node_t* node;
trx_savept_t savept;
trx_t* trx = cursor->prebuilt->trx;
dict_table_t* table = cursor->prebuilt->table;
ib_qry_proc_t* q_proc = &cursor->q_proc;
/* The transaction must be running. */
ut_a(trx->state != TRX_STATE_NOT_STARTED);
node = q_proc->node.upd;
ut_a(dict_index_is_clust(pcur->btr_cur.index));
btr_pcur_copy_stored_position(node->pcur, pcur);
ut_a(node->pcur->rel_pos == BTR_PCUR_ON);
savept = trx_savept_take(trx);
thr = que_fork_get_first_thr(q_proc->grph.upd);
node->state = UPD_NODE_UPDATE_CLUSTERED;
que_thr_move_to_run_state_for_mysql(thr, trx);
err = ib_update_row_with_lock_retry(thr, node, &savept);
if (err == DB_SUCCESS) {
que_thr_stop_for_mysql_no_error(thr, trx);
if (node->is_delete) {
dict_table_n_rows_dec(table);
srv_stats.n_rows_deleted.inc();
} else {
srv_stats.n_rows_updated.inc();
}
} else if (err == DB_RECORD_NOT_FOUND) {
trx->error_state = DB_SUCCESS;
}
trx->op_info = "";
return(err);
}
/*****************************************************************//**
Update a row in a table.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_update_row(
/*=================*/
ib_crsr_t ib_crsr, /*!< in: InnoDB cursor instance */
const ib_tpl_t ib_old_tpl, /*!< in: Old tuple in table */
const ib_tpl_t ib_new_tpl) /*!< in: New tuple to update */
{
upd_t* upd;
ib_err_t err;
btr_pcur_t* pcur;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
row_prebuilt_t* prebuilt = cursor->prebuilt;
const ib_tuple_t*old_tuple = (const ib_tuple_t*) ib_old_tpl;
const ib_tuple_t*new_tuple = (const ib_tuple_t*) ib_new_tpl;
if (dict_index_is_clust(prebuilt->index)) {
pcur = &cursor->prebuilt->pcur;
} else if (prebuilt->need_to_access_clustered) {
pcur = &cursor->prebuilt->clust_pcur;
} else {
return(DB_ERROR);
}
ut_a(old_tuple->type == TPL_TYPE_ROW);
ut_a(new_tuple->type == TPL_TYPE_ROW);
upd = ib_update_vector_create(cursor);
err = ib_calc_diff(cursor, upd, old_tuple, new_tuple);
if (err == DB_SUCCESS) {
/* Note that this is not a delete. */
cursor->q_proc.node.upd->is_delete = FALSE;
err = ib_execute_update_query_graph(cursor, pcur);
}
srv_active_wake_master_thread();
return(err);
}
/**********************************************************************//**
Build the update query graph to delete a row from an index.
@return DB_SUCCESS or err code */
static
ib_err_t
ib_delete_row(
/*==========*/
ib_cursor_t* cursor, /*!< in: current cursor */
btr_pcur_t* pcur, /*!< in: Btree persistent cursor */
const rec_t* rec) /*!< in: record to delete */
{
ulint i;
upd_t* upd;
ib_err_t err;
ib_tuple_t* tuple;
ib_tpl_t ib_tpl;
ulint n_cols;
upd_field_t* upd_field;
ib_bool_t page_format;
dict_table_t* table = cursor->prebuilt->table;
dict_index_t* index = dict_table_get_first_index(table);
n_cols = dict_index_get_n_ordering_defined_by_user(index);
ib_tpl = ib_key_tuple_new(index, n_cols);
if (!ib_tpl) {
return(DB_OUT_OF_MEMORY);
}
tuple = (ib_tuple_t*) ib_tpl;
upd = ib_update_vector_create(cursor);
page_format = dict_table_is_comp(index->table);
ib_read_tuple(rec, page_format, tuple, NULL, NULL);
upd->n_fields = ib_tuple_get_n_cols(ib_tpl);
for (i = 0; i < upd->n_fields; ++i) {
dfield_t* dfield;
upd_field = &upd->fields[i];
dfield = dtuple_get_nth_field(tuple->ptr, i);
dfield_copy_data(&upd_field->new_val, dfield);
upd_field->exp = NULL;
upd_field->orig_len = 0;
upd->info_bits = 0;
upd_field->field_no = dict_col_get_clust_pos(
&table->cols[i], index);
}
/* Note that this is a delete. */
cursor->q_proc.node.upd->is_delete = TRUE;
err = ib_execute_update_query_graph(cursor, pcur);
ib_tuple_delete(ib_tpl);
return(err);
}
/*****************************************************************//**
Delete a row in a table.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_delete_row(
/*=================*/
ib_crsr_t ib_crsr) /*!< in: InnoDB cursor instance */
{
ib_err_t err;
btr_pcur_t* pcur;
dict_index_t* index;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
row_prebuilt_t* prebuilt = cursor->prebuilt;
index = dict_table_get_first_index(prebuilt->index->table);
/* Check whether this is a secondary index cursor */
if (index != prebuilt->index) {
if (prebuilt->need_to_access_clustered) {
pcur = &prebuilt->clust_pcur;
} else {
return(DB_ERROR);
}
} else {
pcur = &prebuilt->pcur;
}
if (ib_btr_cursor_is_positioned(pcur)) {
const rec_t* rec;
ib_bool_t page_format;
mtr_t mtr;
page_format = dict_table_is_comp(index->table);
mtr_start(&mtr);
if (btr_pcur_restore_position(
BTR_SEARCH_LEAF, pcur, &mtr)) {
rec = btr_pcur_get_rec(pcur);
} else {
rec = NULL;
}
mtr_commit(&mtr);
if (rec && !rec_get_deleted_flag(rec, page_format)) {
err = ib_delete_row(cursor, pcur, rec);
} else {
err = DB_RECORD_NOT_FOUND;
}
} else {
err = DB_RECORD_NOT_FOUND;
}
srv_active_wake_master_thread();
return(err);
}
/*****************************************************************//**
Read current row.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_read_row(
/*===============*/
ib_crsr_t ib_crsr, /*!< in: InnoDB cursor instance */
ib_tpl_t ib_tpl, /*!< out: read cols into this tuple */
void** row_buf, /*!< in/out: row buffer */
ib_ulint_t* row_len) /*!< in/out: row buffer len */
{
ib_err_t err;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
ut_a(cursor->prebuilt->trx->state != TRX_STATE_NOT_STARTED);
/* When searching with IB_EXACT_MATCH set, row_search_for_mysql()
will not position the persistent cursor but will copy the record
found into the row cache. It should be the only entry. */
if (!ib_cursor_is_positioned(ib_crsr) ) {
err = DB_RECORD_NOT_FOUND;
} else {
mtr_t mtr;
btr_pcur_t* pcur;
row_prebuilt_t* prebuilt = cursor->prebuilt;
if (prebuilt->need_to_access_clustered
&& tuple->type == TPL_TYPE_ROW) {
pcur = &prebuilt->clust_pcur;
} else {
pcur = &prebuilt->pcur;
}
if (pcur == NULL) {
return(DB_ERROR);
}
mtr_start(&mtr);
if (btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, &mtr)) {
const rec_t* rec;
ib_bool_t page_format;
page_format = dict_table_is_comp(tuple->index->table);
rec = btr_pcur_get_rec(pcur);
if (prebuilt->innodb_api_rec &&
prebuilt->innodb_api_rec != rec) {
rec = prebuilt->innodb_api_rec;
}
if (!rec_get_deleted_flag(rec, page_format)) {
ib_read_tuple(rec, page_format, tuple,
row_buf, (ulint*) row_len);
err = DB_SUCCESS;
} else{
err = DB_RECORD_NOT_FOUND;
}
} else {
err = DB_RECORD_NOT_FOUND;
}
mtr_commit(&mtr);
}
return(err);
}
/*****************************************************************//**
Move cursor to the first record in the table.
@return DB_SUCCESS or err code */
UNIV_INLINE
ib_err_t
ib_cursor_position(
/*===============*/
ib_cursor_t* cursor, /*!< in: InnoDB cursor instance */
ib_srch_mode_t mode) /*!< in: Search mode */
{
ib_err_t err;
row_prebuilt_t* prebuilt = cursor->prebuilt;
unsigned char* buf;
buf = static_cast<unsigned char*>(mem_alloc(UNIV_PAGE_SIZE));
/* We want to position at one of the ends, row_search_for_mysql()
uses the search_tuple fields to work out what to do. */
dtuple_set_n_fields(prebuilt->search_tuple, 0);
err = static_cast<ib_err_t>(row_search_for_mysql(
buf, mode, prebuilt, 0, 0));
mem_free(buf);
return(err);
}
/*****************************************************************//**
Move cursor to the first record in the table.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_first(
/*============*/
ib_crsr_t ib_crsr) /*!< in: InnoDB cursor instance */
{
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
return(ib_cursor_position(cursor, IB_CUR_G));
}
/*****************************************************************//**
Move cursor to the last record in the table.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_last(
/*===========*/
ib_crsr_t ib_crsr) /*!< in: InnoDB cursor instance */
{
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
return(ib_cursor_position(cursor, IB_CUR_L));
}
/*****************************************************************//**
Move cursor to the next user record in the table.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_next(
/*===========*/
ib_crsr_t ib_crsr) /*!< in: InnoDB cursor instance */
{
ib_err_t err;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
row_prebuilt_t* prebuilt = cursor->prebuilt;
byte buf[UNIV_PAGE_SIZE_MAX];
/* We want to move to the next record */
dtuple_set_n_fields(prebuilt->search_tuple, 0);
err = static_cast<ib_err_t>(row_search_for_mysql(
buf, PAGE_CUR_G, prebuilt, 0, ROW_SEL_NEXT));
return(err);
}
/*****************************************************************//**
Search for key.
@return DB_SUCCESS or err code */
UNIV_INTERN
ib_err_t
ib_cursor_moveto(
/*=============*/
ib_crsr_t ib_crsr, /*!< in: InnoDB cursor instance */
ib_tpl_t ib_tpl, /*!< in: Key to search for */
ib_srch_mode_t ib_srch_mode) /*!< in: search mode */
{
ulint i;
ulint n_fields;
ib_err_t err = DB_SUCCESS;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
row_prebuilt_t* prebuilt = cursor->prebuilt;
dtuple_t* search_tuple = prebuilt->search_tuple;
unsigned char* buf;
ut_a(tuple->type == TPL_TYPE_KEY);
n_fields = dict_index_get_n_ordering_defined_by_user(prebuilt->index);
dtuple_set_n_fields(search_tuple, n_fields);
dtuple_set_n_fields_cmp(search_tuple, n_fields);
/* Do a shallow copy */
for (i = 0; i < n_fields; ++i) {
dfield_copy(dtuple_get_nth_field(search_tuple, i),
dtuple_get_nth_field(tuple->ptr, i));
}
ut_a(prebuilt->select_lock_type <= LOCK_NUM);
prebuilt->innodb_api_rec = NULL;
buf = static_cast<unsigned char*>(mem_alloc(UNIV_PAGE_SIZE));
err = static_cast<ib_err_t>(row_search_for_mysql(
buf, ib_srch_mode, prebuilt, cursor->match_mode, 0));
mem_free(buf);
return(err);
}
/*****************************************************************//**
Set the cursor search mode. */
UNIV_INTERN
void
ib_cursor_set_match_mode(
/*=====================*/
ib_crsr_t ib_crsr, /*!< in: Cursor instance */
ib_match_mode_t match_mode) /*!< in: ib_cursor_moveto match mode */
{
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
cursor->match_mode = match_mode;
}
/*****************************************************************//**
Get the dfield instance for the column in the tuple.
@return dfield instance in tuple */
UNIV_INLINE
dfield_t*
ib_col_get_dfield(
/*==============*/
ib_tuple_t* tuple, /*!< in: tuple instance */
ulint col_no) /*!< in: col no. in tuple */
{
dfield_t* dfield;
dfield = dtuple_get_nth_field(tuple->ptr, col_no);
return(dfield);
}
/*****************************************************************//**
Predicate to check whether a column type contains variable length data.
@return DB_SUCCESS or error code */
UNIV_INLINE
ib_err_t
ib_col_is_capped(
/*==============*/
const dtype_t* dtype) /*!< in: column type */
{
return(static_cast<ib_err_t>(
(dtype_get_mtype(dtype) == DATA_VARCHAR
|| dtype_get_mtype(dtype) == DATA_CHAR
|| dtype_get_mtype(dtype) == DATA_MYSQL
|| dtype_get_mtype(dtype) == DATA_VARMYSQL
|| dtype_get_mtype(dtype) == DATA_FIXBINARY
|| dtype_get_mtype(dtype) == DATA_BINARY)
&& dtype_get_len(dtype) > 0));
}
/*****************************************************************//**
Set a column of the tuple. Make a copy using the tuple's heap.
@return DB_SUCCESS or error code */
UNIV_INTERN
ib_err_t
ib_col_set_value(
/*=============*/
ib_tpl_t ib_tpl, /*!< in: tuple instance */
ib_ulint_t col_no, /*!< in: column index in tuple */
const void* src, /*!< in: data value */
ib_ulint_t len, /*!< in: data value len */
ib_bool_t need_cpy) /*!< in: if need memcpy */
{
const dtype_t* dtype;
dfield_t* dfield;
void* dst = NULL;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
ulint col_len;
dfield = ib_col_get_dfield(tuple, col_no);
/* User wants to set the column to NULL. */
if (len == IB_SQL_NULL) {
dfield_set_null(dfield);
return(DB_SUCCESS);
}
dtype = dfield_get_type(dfield);
col_len = dtype_get_len(dtype);
/* Not allowed to update system columns. */
if (dtype_get_mtype(dtype) == DATA_SYS) {
return(DB_DATA_MISMATCH);
}
dst = dfield_get_data(dfield);
/* Since TEXT/CLOB also map to DATA_VARCHAR we need to make an
exception. Perhaps we need to set the precise type and check
for that. */
if (ib_col_is_capped(dtype)) {
len = ut_min(len, col_len);
if (dst == NULL || len > dfield_get_len(dfield)) {
dst = mem_heap_alloc(tuple->heap, col_len);
ut_a(dst != NULL);
}
} else if (dst == NULL || len > dfield_get_len(dfield)) {
dst = mem_heap_alloc(tuple->heap, len);
}
if (dst == NULL) {
return(DB_OUT_OF_MEMORY);
}
switch (dtype_get_mtype(dtype)) {
case DATA_INT: {
if (col_len == len) {
ibool usign;
usign = dtype_get_prtype(dtype) & DATA_UNSIGNED;
mach_write_int_type(static_cast<byte*>(dst),
static_cast<const byte*>(src),
len, usign);
} else {
return(DB_DATA_MISMATCH);
}
break;
}
case DATA_FLOAT:
if (len == sizeof(float)) {
mach_float_write(static_cast<byte*>(dst), *(float*)src);
} else {
return(DB_DATA_MISMATCH);
}
break;
case DATA_DOUBLE:
if (len == sizeof(double)) {
mach_double_write(static_cast<byte*>(dst),
*(double*)src);
} else {
return(DB_DATA_MISMATCH);
}
break;
case DATA_SYS:
ut_error;
break;
case DATA_CHAR: {
ulint pad_char = ULINT_UNDEFINED;
pad_char = dtype_get_pad_char(
dtype_get_mtype(dtype), dtype_get_prtype(dtype));
ut_a(pad_char != ULINT_UNDEFINED);
memset((byte*) dst + len,
pad_char,
col_len - len);
memcpy(dst, src, len);
len = col_len;
break;
}
case DATA_BLOB:
case DATA_BINARY:
case DATA_DECIMAL:
case DATA_VARCHAR:
case DATA_FIXBINARY:
if (need_cpy) {
memcpy(dst, src, len);
} else {
dfield_set_data(dfield, src, len);
dst = dfield_get_data(dfield);
}
break;
case DATA_MYSQL:
case DATA_VARMYSQL: {
ulint cset;
CHARSET_INFO* cs;
int error = 0;
ulint true_len = len;
/* For multi byte character sets we need to
calculate the true length of the data. */
cset = dtype_get_charset_coll(
dtype_get_prtype(dtype));
cs = all_charsets[cset];
if (cs) {
uint pos = (uint)(col_len / cs->mbmaxlen);
if (len > 0 && cs->mbmaxlen > 1) {
true_len = (ulint)
cs->cset->well_formed_len(
cs,
(const char*)src,
(const char*)src + len,
pos,
&error);
if (true_len < len) {
len = true_len;
}
}
}
/* All invalid bytes in data need be truncated.
If len == 0, means all bytes of the data is invalid.
In this case, the data will be truncated to empty.*/
memcpy(dst, src, len);
/* For DATA_MYSQL, need to pad the unused
space with spaces. */
if (dtype_get_mtype(dtype) == DATA_MYSQL) {
ulint n_chars;
if (len < col_len) {
ulint pad_len = col_len - len;
ut_a(cs != NULL);
ut_a(!(pad_len % cs->mbminlen));
cs->cset->fill(cs, (char*)dst + len,
pad_len,
0x20 /* space */);
}
/* Why we should do below? See function
row_mysql_store_col_in_innobase_format */
ut_a(!(dtype_get_len(dtype)
% dtype_get_mbmaxlen(dtype)));
n_chars = dtype_get_len(dtype)
/ dtype_get_mbmaxlen(dtype);
/* Strip space padding. */
while (col_len > n_chars
&& ((char*)dst)[col_len - 1] == 0x20) {
col_len--;
}
len = col_len;
}
break;
}
default:
ut_error;
}
if (dst != dfield_get_data(dfield)) {
dfield_set_data(dfield, dst, len);
} else {
dfield_set_len(dfield, len);
}
return(DB_SUCCESS);
}
/*****************************************************************//**
Get the size of the data available in a column of the tuple.
@return bytes avail or IB_SQL_NULL */
UNIV_INTERN
ib_ulint_t
ib_col_get_len(
/*===========*/
ib_tpl_t ib_tpl, /*!< in: tuple instance */
ib_ulint_t i) /*!< in: column index in tuple */
{
const dfield_t* dfield;
ulint data_len;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
dfield = ib_col_get_dfield(tuple, i);
data_len = dfield_get_len(dfield);
return(data_len == UNIV_SQL_NULL ? IB_SQL_NULL : data_len);
}
/*****************************************************************//**
Copy a column value from the tuple.
@return bytes copied or IB_SQL_NULL */
UNIV_INLINE
ib_ulint_t
ib_col_copy_value_low(
/*==================*/
ib_tpl_t ib_tpl, /*!< in: tuple instance */
ib_ulint_t i, /*!< in: column index in tuple */
void* dst, /*!< out: copied data value */
ib_ulint_t len) /*!< in: max data value len to copy */
{
const void* data;
const dfield_t* dfield;
ulint data_len;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
dfield = ib_col_get_dfield(tuple, i);
data = dfield_get_data(dfield);
data_len = dfield_get_len(dfield);
if (data_len != UNIV_SQL_NULL) {
const dtype_t* dtype = dfield_get_type(dfield);
switch (dtype_get_mtype(dfield_get_type(dfield))) {
case DATA_INT: {
ibool usign;
ullint ret;
ut_a(data_len == len);
usign = dtype_get_prtype(dtype) & DATA_UNSIGNED;
ret = mach_read_int_type(static_cast<const byte*>(data),
data_len, usign);
if (usign) {
if (len == 1) {
*(ib_i8_t*)dst = (ib_i8_t)ret;
} else if (len == 2) {
*(ib_i16_t*)dst = (ib_i16_t)ret;
} else if (len == 4) {
*(ib_i32_t*)dst = (ib_i32_t)ret;
} else {
*(ib_i64_t*)dst = (ib_i64_t)ret;
}
} else {
if (len == 1) {
*(ib_u8_t*)dst = (ib_i8_t)ret;
} else if (len == 2) {
*(ib_u16_t*)dst = (ib_i16_t)ret;
} else if (len == 4) {
*(ib_u32_t*)dst = (ib_i32_t)ret;
} else {
*(ib_u64_t*)dst = (ib_i64_t)ret;
}
}
break;
}
case DATA_FLOAT:
if (len == data_len) {
float f;
ut_a(data_len == sizeof(f));
f = mach_float_read(static_cast<const byte*>(
data));
memcpy(dst, &f, sizeof(f));
} else {
data_len = 0;
}
break;
case DATA_DOUBLE:
if (len == data_len) {
double d;
ut_a(data_len == sizeof(d));
d = mach_double_read(static_cast<const byte*>(
data));
memcpy(dst, &d, sizeof(d));
} else {
data_len = 0;
}
break;
default:
data_len = ut_min(data_len, len);
memcpy(dst, data, data_len);
}
} else {
data_len = IB_SQL_NULL;
}
return(data_len);
}
/*****************************************************************//**
Copy a column value from the tuple.
@return bytes copied or IB_SQL_NULL */
UNIV_INTERN
ib_ulint_t
ib_col_copy_value(
/*==============*/
ib_tpl_t ib_tpl, /*!< in: tuple instance */
ib_ulint_t i, /*!< in: column index in tuple */
void* dst, /*!< out: copied data value */
ib_ulint_t len) /*!< in: max data value len to copy */
{
return(ib_col_copy_value_low(ib_tpl, i, dst, len));
}
/*****************************************************************//**
Get the InnoDB column attribute from the internal column precise type.
@return precise type in api format */
UNIV_INLINE
ib_col_attr_t
ib_col_get_attr(
/*============*/
ulint prtype) /*!< in: column definition */
{
ib_col_attr_t attr = IB_COL_NONE;
if (prtype & DATA_UNSIGNED) {
attr = static_cast<ib_col_attr_t>(attr | IB_COL_UNSIGNED);
}
if (prtype & DATA_NOT_NULL) {
attr = static_cast<ib_col_attr_t>(attr | IB_COL_NOT_NULL);
}
return(attr);
}
/*****************************************************************//**
Get a column name from the tuple.
@return name of the column */
UNIV_INTERN
const char*
ib_col_get_name(
/*============*/
ib_crsr_t ib_crsr, /*!< in: InnoDB cursor instance */
ib_ulint_t i) /*!< in: column index in tuple */
{
const char* name;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
dict_table_t* table = cursor->prebuilt->table;
dict_col_t* col = dict_table_get_nth_col(table, i);
ulint col_no = dict_col_get_no(col);
name = dict_table_get_col_name(table, col_no);
return(name);
}
/*****************************************************************//**
Get an index field name from the cursor.
@return name of the field */
UNIV_INTERN
const char*
ib_get_idx_field_name(
/*==================*/
ib_crsr_t ib_crsr, /*!< in: InnoDB cursor instance */
ib_ulint_t i) /*!< in: column index in tuple */
{
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
dict_index_t* index = cursor->prebuilt->index;
dict_field_t* field;
if (index) {
field = dict_index_get_nth_field(cursor->prebuilt->index, i);
if (field) {
return(field->name);
}
}
return(NULL);
}
/*****************************************************************//**
Get a column type, length and attributes from the tuple.
@return len of column data */
UNIV_INLINE
ib_ulint_t
ib_col_get_meta_low(
/*================*/
ib_tpl_t ib_tpl, /*!< in: tuple instance */
ib_ulint_t i, /*!< in: column index in tuple */
ib_col_meta_t* ib_col_meta) /*!< out: column meta data */
{
ib_u16_t prtype;
const dfield_t* dfield;
ulint data_len;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
dfield = ib_col_get_dfield(tuple, i);
data_len = dfield_get_len(dfield);
/* We assume 1-1 mapping between the ENUM and internal type codes. */
ib_col_meta->type = static_cast<ib_col_type_t>(
dtype_get_mtype(dfield_get_type(dfield)));
ib_col_meta->type_len = dtype_get_len(dfield_get_type(dfield));
prtype = (ib_u16_t) dtype_get_prtype(dfield_get_type(dfield));
ib_col_meta->attr = ib_col_get_attr(prtype);
ib_col_meta->client_type = prtype & DATA_MYSQL_TYPE_MASK;
return(data_len);
}
/*************************************************************//**
Read a signed int 8 bit column from an InnoDB tuple. */
UNIV_INLINE
ib_err_t
ib_tuple_check_int(
/*===============*/
ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */
ib_ulint_t i, /*!< in: column number */
ib_bool_t usign, /*!< in: true if unsigned */
ulint size) /*!< in: size of integer */
{
ib_col_meta_t ib_col_meta;
ib_col_get_meta_low(ib_tpl, i, &ib_col_meta);
if (ib_col_meta.type != IB_INT) {
return(DB_DATA_MISMATCH);
} else if (ib_col_meta.type_len == IB_SQL_NULL) {
return(DB_UNDERFLOW);
} else if (ib_col_meta.type_len != size) {
return(DB_DATA_MISMATCH);
} else if ((ib_col_meta.attr & IB_COL_UNSIGNED) && !usign) {
return(DB_DATA_MISMATCH);
}
return(DB_SUCCESS);
}
/*************************************************************//**
Read a signed int 8 bit column from an InnoDB tuple.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_read_i8(
/*=============*/
ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */
ib_ulint_t i, /*!< in: column number */
ib_i8_t* ival) /*!< out: integer value */
{
ib_err_t err;
err = ib_tuple_check_int(ib_tpl, i, IB_FALSE, sizeof(*ival));
if (err == DB_SUCCESS) {
ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival));
}
return(err);
}
/*************************************************************//**
Read an unsigned int 8 bit column from an InnoDB tuple.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_read_u8(
/*=============*/
ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */
ib_ulint_t i, /*!< in: column number */
ib_u8_t* ival) /*!< out: integer value */
{
ib_err_t err;
err = ib_tuple_check_int(ib_tpl, i, IB_TRUE, sizeof(*ival));
if (err == DB_SUCCESS) {
ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival));
}
return(err);
}
/*************************************************************//**
Read a signed int 16 bit column from an InnoDB tuple.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_read_i16(
/*==============*/
ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */
ib_ulint_t i, /*!< in: column number */
ib_i16_t* ival) /*!< out: integer value */
{
ib_err_t err;
err = ib_tuple_check_int(ib_tpl, i, FALSE, sizeof(*ival));
if (err == DB_SUCCESS) {
ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival));
}
return(err);
}
/*************************************************************//**
Read an unsigned int 16 bit column from an InnoDB tuple.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_read_u16(
/*==============*/
ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */
ib_ulint_t i, /*!< in: column number */
ib_u16_t* ival) /*!< out: integer value */
{
ib_err_t err;
err = ib_tuple_check_int(ib_tpl, i, IB_TRUE, sizeof(*ival));
if (err == DB_SUCCESS) {
ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival));
}
return(err);
}
/*************************************************************//**
Read a signed int 32 bit column from an InnoDB tuple.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_read_i32(
/*==============*/
ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */
ib_ulint_t i, /*!< in: column number */
ib_i32_t* ival) /*!< out: integer value */
{
ib_err_t err;
err = ib_tuple_check_int(ib_tpl, i, FALSE, sizeof(*ival));
if (err == DB_SUCCESS) {
ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival));
}
return(err);
}
/*************************************************************//**
Read an unsigned int 32 bit column from an InnoDB tuple.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_read_u32(
/*==============*/
ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */
ib_ulint_t i, /*!< in: column number */
ib_u32_t* ival) /*!< out: integer value */
{
ib_err_t err;
err = ib_tuple_check_int(ib_tpl, i, IB_TRUE, sizeof(*ival));
if (err == DB_SUCCESS) {
ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival));
}
return(err);
}
/*************************************************************//**
Read a signed int 64 bit column from an InnoDB tuple.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_read_i64(
/*==============*/
ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */
ib_ulint_t i, /*!< in: column number */
ib_i64_t* ival) /*!< out: integer value */
{
ib_err_t err;
err = ib_tuple_check_int(ib_tpl, i, FALSE, sizeof(*ival));
if (err == DB_SUCCESS) {
ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival));
}
return(err);
}
/*************************************************************//**
Read an unsigned int 64 bit column from an InnoDB tuple.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_read_u64(
/*==============*/
ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */
ib_ulint_t i, /*!< in: column number */
ib_u64_t* ival) /*!< out: integer value */
{
ib_err_t err;
err = ib_tuple_check_int(ib_tpl, i, IB_TRUE, sizeof(*ival));
if (err == DB_SUCCESS) {
ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival));
}
return(err);
}
/*****************************************************************//**
Get a column value pointer from the tuple.
@return NULL or pointer to buffer */
UNIV_INTERN
const void*
ib_col_get_value(
/*=============*/
ib_tpl_t ib_tpl, /*!< in: tuple instance */
ib_ulint_t i) /*!< in: column index in tuple */
{
const void* data;
const dfield_t* dfield;
ulint data_len;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
dfield = ib_col_get_dfield(tuple, i);
data = dfield_get_data(dfield);
data_len = dfield_get_len(dfield);
return(data_len != UNIV_SQL_NULL ? data : NULL);
}
/*****************************************************************//**
Get a column type, length and attributes from the tuple.
@return len of column data */
UNIV_INTERN
ib_ulint_t
ib_col_get_meta(
/*============*/
ib_tpl_t ib_tpl, /*!< in: tuple instance */
ib_ulint_t i, /*!< in: column index in tuple */
ib_col_meta_t* ib_col_meta) /*!< out: column meta data */
{
return(ib_col_get_meta_low(ib_tpl, i, ib_col_meta));
}
/*****************************************************************//**
"Clear" or reset an InnoDB tuple. We free the heap and recreate the tuple.
@return new tuple, or NULL */
UNIV_INTERN
ib_tpl_t
ib_tuple_clear(
/*============*/
ib_tpl_t ib_tpl) /*!< in,own: tuple (will be freed) */
{
const dict_index_t* index;
ulint n_cols;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
ib_tuple_type_t type = tuple->type;
mem_heap_t* heap = tuple->heap;
index = tuple->index;
n_cols = dtuple_get_n_fields(tuple->ptr);
mem_heap_empty(heap);
if (type == TPL_TYPE_ROW) {
return(ib_row_tuple_new_low(index, n_cols, heap));
} else {
return(ib_key_tuple_new_low(index, n_cols, heap));
}
}
/*****************************************************************//**
Create a new cluster key search tuple and copy the contents of the
secondary index key tuple columns that refer to the cluster index record
to the cluster key. It does a deep copy of the column data.
@return DB_SUCCESS or error code */
UNIV_INTERN
ib_err_t
ib_tuple_get_cluster_key(
/*=====================*/
ib_crsr_t ib_crsr, /*!< in: secondary index cursor */
ib_tpl_t* ib_dst_tpl, /*!< out,own: destination tuple */
const ib_tpl_t ib_src_tpl) /*!< in: source tuple */
{
ulint i;
ulint n_fields;
ib_err_t err = DB_SUCCESS;
ib_tuple_t* dst_tuple = NULL;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
ib_tuple_t* src_tuple = (ib_tuple_t*) ib_src_tpl;
dict_index_t* clust_index;
clust_index = dict_table_get_first_index(cursor->prebuilt->table);
/* We need to ensure that the src tuple belongs to the same table
as the open cursor and that it's not a tuple for a cluster index. */
if (src_tuple->type != TPL_TYPE_KEY) {
return(DB_ERROR);
} else if (src_tuple->index->table != cursor->prebuilt->table) {
return(DB_DATA_MISMATCH);
} else if (src_tuple->index == clust_index) {
return(DB_ERROR);
}
/* Create the cluster index key search tuple. */
*ib_dst_tpl = ib_clust_search_tuple_create(ib_crsr);
if (!*ib_dst_tpl) {
return(DB_OUT_OF_MEMORY);
}
dst_tuple = (ib_tuple_t*) *ib_dst_tpl;
ut_a(dst_tuple->index == clust_index);
n_fields = dict_index_get_n_unique(dst_tuple->index);
/* Do a deep copy of the data fields. */
for (i = 0; i < n_fields; i++) {
ulint pos;
dfield_t* src_field;
dfield_t* dst_field;
pos = dict_index_get_nth_field_pos(
src_tuple->index, dst_tuple->index, i);
ut_a(pos != ULINT_UNDEFINED);
src_field = dtuple_get_nth_field(src_tuple->ptr, pos);
dst_field = dtuple_get_nth_field(dst_tuple->ptr, i);
if (!dfield_is_null(src_field)) {
UNIV_MEM_ASSERT_RW(src_field->data, src_field->len);
dst_field->data = mem_heap_dup(
dst_tuple->heap,
src_field->data,
src_field->len);
dst_field->len = src_field->len;
} else {
dfield_set_null(dst_field);
}
}
return(err);
}
/*****************************************************************//**
Copy the contents of source tuple to destination tuple. The tuples
must be of the same type and belong to the same table/index.
@return DB_SUCCESS or error code */
UNIV_INTERN
ib_err_t
ib_tuple_copy(
/*==========*/
ib_tpl_t ib_dst_tpl, /*!< in: destination tuple */
const ib_tpl_t ib_src_tpl) /*!< in: source tuple */
{
ulint i;
ulint n_fields;
ib_err_t err = DB_SUCCESS;
const ib_tuple_t*src_tuple = (const ib_tuple_t*) ib_src_tpl;
ib_tuple_t* dst_tuple = (ib_tuple_t*) ib_dst_tpl;
/* Make sure src and dst are not the same. */
ut_a(src_tuple != dst_tuple);
/* Make sure they are the same type and refer to the same index. */
if (src_tuple->type != dst_tuple->type
|| src_tuple->index != dst_tuple->index) {
return(DB_DATA_MISMATCH);
}
n_fields = dtuple_get_n_fields(src_tuple->ptr);
ut_ad(n_fields == dtuple_get_n_fields(dst_tuple->ptr));
/* Do a deep copy of the data fields. */
for (i = 0; i < n_fields; ++i) {
dfield_t* src_field;
dfield_t* dst_field;
src_field = dtuple_get_nth_field(src_tuple->ptr, i);
dst_field = dtuple_get_nth_field(dst_tuple->ptr, i);
if (!dfield_is_null(src_field)) {
UNIV_MEM_ASSERT_RW(src_field->data, src_field->len);
dst_field->data = mem_heap_dup(
dst_tuple->heap,
src_field->data,
src_field->len);
dst_field->len = src_field->len;
} else {
dfield_set_null(dst_field);
}
}
return(err);
}
/*****************************************************************//**
Create an InnoDB tuple used for index/table search.
@return own: Tuple for current index */
UNIV_INTERN
ib_tpl_t
ib_sec_search_tuple_create(
/*=======================*/
ib_crsr_t ib_crsr) /*!< in: Cursor instance */
{
ulint n_cols;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
dict_index_t* index = cursor->prebuilt->index;
n_cols = dict_index_get_n_unique_in_tree(index);
return(ib_key_tuple_new(index, n_cols));
}
/*****************************************************************//**
Create an InnoDB tuple used for index/table search.
@return own: Tuple for current index */
UNIV_INTERN
ib_tpl_t
ib_sec_read_tuple_create(
/*=====================*/
ib_crsr_t ib_crsr) /*!< in: Cursor instance */
{
ulint n_cols;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
dict_index_t* index = cursor->prebuilt->index;
n_cols = dict_index_get_n_fields(index);
return(ib_row_tuple_new(index, n_cols));
}
/*****************************************************************//**
Create an InnoDB tuple used for table key operations.
@return own: Tuple for current table */
UNIV_INTERN
ib_tpl_t
ib_clust_search_tuple_create(
/*=========================*/
ib_crsr_t ib_crsr) /*!< in: Cursor instance */
{
ulint n_cols;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
dict_index_t* index;
index = dict_table_get_first_index(cursor->prebuilt->table);
n_cols = dict_index_get_n_ordering_defined_by_user(index);
return(ib_key_tuple_new(index, n_cols));
}
/*****************************************************************//**
Create an InnoDB tuple for table row operations.
@return own: Tuple for current table */
UNIV_INTERN
ib_tpl_t
ib_clust_read_tuple_create(
/*=======================*/
ib_crsr_t ib_crsr) /*!< in: Cursor instance */
{
ulint n_cols;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
dict_index_t* index;
index = dict_table_get_first_index(cursor->prebuilt->table);
n_cols = dict_table_get_n_cols(cursor->prebuilt->table);
return(ib_row_tuple_new(index, n_cols));
}
/*****************************************************************//**
Return the number of user columns in the tuple definition.
@return number of user columns */
UNIV_INTERN
ib_ulint_t
ib_tuple_get_n_user_cols(
/*=====================*/
const ib_tpl_t ib_tpl) /*!< in: Tuple for current table */
{
const ib_tuple_t* tuple = (const ib_tuple_t*) ib_tpl;
if (tuple->type == TPL_TYPE_ROW) {
return(dict_table_get_n_user_cols(tuple->index->table));
}
return(dict_index_get_n_ordering_defined_by_user(tuple->index));
}
/*****************************************************************//**
Return the number of columns in the tuple definition.
@return number of columns */
UNIV_INTERN
ib_ulint_t
ib_tuple_get_n_cols(
/*================*/
const ib_tpl_t ib_tpl) /*!< in: Tuple for table/index */
{
const ib_tuple_t* tuple = (const ib_tuple_t*) ib_tpl;
return(dtuple_get_n_fields(tuple->ptr));
}
/*****************************************************************//**
Destroy an InnoDB tuple. */
UNIV_INTERN
void
ib_tuple_delete(
/*============*/
ib_tpl_t ib_tpl) /*!< in,own: Tuple instance to delete */
{
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
if (!ib_tpl) {
return;
}
mem_heap_free(tuple->heap);
}
/*****************************************************************//**
Get a table id. This function will acquire the dictionary mutex.
@return DB_SUCCESS if found */
UNIV_INTERN
ib_err_t
ib_table_get_id(
/*============*/
const char* table_name, /*!< in: table to find */
ib_id_u64_t* table_id) /*!< out: table id if found */
{
ib_err_t err;
dict_mutex_enter_for_mysql();
err = ib_table_get_id_low(table_name, table_id);
dict_mutex_exit_for_mysql();
return(err);
}
/*****************************************************************//**
Get an index id.
@return DB_SUCCESS if found */
UNIV_INTERN
ib_err_t
ib_index_get_id(
/*============*/
const char* table_name, /*!< in: find index for this table */
const char* index_name, /*!< in: index to find */
ib_id_u64_t* index_id) /*!< out: index id if found */
{
dict_table_t* table;
char* normalized_name;
ib_err_t err = DB_TABLE_NOT_FOUND;
*index_id = 0;
normalized_name = static_cast<char*>(
mem_alloc(ut_strlen(table_name) + 1));
ib_normalize_table_name(normalized_name, table_name);
table = ib_lookup_table_by_name(normalized_name);
mem_free(normalized_name);
normalized_name = NULL;
if (table != NULL) {
dict_index_t* index;
index = dict_table_get_index_on_name(table, index_name);
if (index != NULL) {
/* We only support 32 bit table and index ids. Because
we need to pack the table id into the index id. */
*index_id = (table->id);
*index_id <<= 32;
*index_id |= (index->id);
err = DB_SUCCESS;
}
}
return(err);
}
#ifdef __WIN__
#define SRV_PATH_SEPARATOR '\\'
#else
#define SRV_PATH_SEPARATOR '/'
#endif
/*****************************************************************//**
Check if cursor is positioned.
@return IB_TRUE if positioned */
UNIV_INTERN
ib_bool_t
ib_cursor_is_positioned(
/*====================*/
const ib_crsr_t ib_crsr) /*!< in: InnoDB cursor instance */
{
const ib_cursor_t* cursor = (const ib_cursor_t*) ib_crsr;
row_prebuilt_t* prebuilt = cursor->prebuilt;
return(ib_btr_cursor_is_positioned(&prebuilt->pcur));
}
/*****************************************************************//**
Checks if the data dictionary is latched in exclusive mode.
@return TRUE if exclusive latch */
UNIV_INTERN
ib_bool_t
ib_schema_lock_is_exclusive(
/*========================*/
const ib_trx_t ib_trx) /*!< in: transaction */
{
const trx_t* trx = (const trx_t*) ib_trx;
return(trx->dict_operation_lock_mode == RW_X_LATCH);
}
/*****************************************************************//**
Checks if the data dictionary is latched in shared mode.
@return TRUE if shared latch */
UNIV_INTERN
ib_bool_t
ib_schema_lock_is_shared(
/*=====================*/
const ib_trx_t ib_trx) /*!< in: transaction */
{
const trx_t* trx = (const trx_t*) ib_trx;
return(trx->dict_operation_lock_mode == RW_S_LATCH);
}
/*****************************************************************//**
Set the Lock an InnoDB cursor/table.
@return DB_SUCCESS or error code */
UNIV_INTERN
ib_err_t
ib_cursor_lock(
/*===========*/
ib_crsr_t ib_crsr, /*!< in/out: InnoDB cursor */
ib_lck_mode_t ib_lck_mode) /*!< in: InnoDB lock mode */
{
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
row_prebuilt_t* prebuilt = cursor->prebuilt;
trx_t* trx = prebuilt->trx;
dict_table_t* table = prebuilt->table;
return(ib_trx_lock_table_with_retry(
trx, table, (enum lock_mode) ib_lck_mode));
}
/*****************************************************************//**
Set the Lock an InnoDB table using the table id.
@return DB_SUCCESS or error code */
UNIV_INTERN
ib_err_t
ib_table_lock(
/*==========*/
ib_trx_t ib_trx, /*!< in/out: transaction */
ib_id_u64_t table_id, /*!< in: table id */
ib_lck_mode_t ib_lck_mode) /*!< in: InnoDB lock mode */
{
ib_err_t err;
que_thr_t* thr;
mem_heap_t* heap;
dict_table_t* table;
ib_qry_proc_t q_proc;
trx_t* trx = (trx_t*) ib_trx;
ut_a(trx->state != TRX_STATE_NOT_STARTED);
table = ib_open_table_by_id(table_id, FALSE);
if (table == NULL) {
return(DB_TABLE_NOT_FOUND);
}
ut_a(ib_lck_mode <= static_cast<ib_lck_mode_t>(LOCK_NUM));
heap = mem_heap_create(128);
q_proc.node.sel = sel_node_create(heap);
thr = pars_complete_graph_for_exec(q_proc.node.sel, trx, heap);
q_proc.grph.sel = static_cast<que_fork_t*>(que_node_get_parent(thr));
q_proc.grph.sel->state = QUE_FORK_ACTIVE;
trx->op_info = "setting table lock";
ut_a(ib_lck_mode == IB_LOCK_IS || ib_lck_mode == IB_LOCK_IX);
err = static_cast<ib_err_t>(
lock_table(0, table, (enum lock_mode) ib_lck_mode, thr));
trx->error_state = err;
mem_heap_free(heap);
return(err);
}
/*****************************************************************//**
Unlock an InnoDB table.
@return DB_SUCCESS or error code */
UNIV_INTERN
ib_err_t
ib_cursor_unlock(
/*=============*/
ib_crsr_t ib_crsr) /*!< in/out: InnoDB cursor */
{
ib_err_t err = DB_SUCCESS;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
row_prebuilt_t* prebuilt = cursor->prebuilt;
if (prebuilt->trx->mysql_n_tables_locked > 0) {
--prebuilt->trx->mysql_n_tables_locked;
} else {
err = DB_ERROR;
}
return(err);
}
/*****************************************************************//**
Set the Lock mode of the cursor.
@return DB_SUCCESS or error code */
UNIV_INTERN
ib_err_t
ib_cursor_set_lock_mode(
/*====================*/
ib_crsr_t ib_crsr, /*!< in/out: InnoDB cursor */
ib_lck_mode_t ib_lck_mode) /*!< in: InnoDB lock mode */
{
ib_err_t err = DB_SUCCESS;
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
row_prebuilt_t* prebuilt = cursor->prebuilt;
ut_a(ib_lck_mode <= static_cast<ib_lck_mode_t>(LOCK_NUM));
if (ib_lck_mode == IB_LOCK_X) {
err = ib_cursor_lock(ib_crsr, IB_LOCK_IX);
} else if (ib_lck_mode == IB_LOCK_S) {
err = ib_cursor_lock(ib_crsr, IB_LOCK_IS);
}
if (err == DB_SUCCESS) {
prebuilt->select_lock_type = (enum lock_mode) ib_lck_mode;
ut_a(prebuilt->trx->state != TRX_STATE_NOT_STARTED);
}
return(err);
}
/*****************************************************************//**
Set need to access clustered index record. */
UNIV_INTERN
void
ib_cursor_set_cluster_access(
/*=========================*/
ib_crsr_t ib_crsr) /*!< in/out: InnoDB cursor */
{
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
row_prebuilt_t* prebuilt = cursor->prebuilt;
prebuilt->need_to_access_clustered = TRUE;
}
/*************************************************************//**
Convert and write an INT column value to an InnoDB tuple.
@return DB_SUCCESS or error */
UNIV_INLINE
ib_err_t
ib_tuple_write_int(
/*===============*/
ib_tpl_t ib_tpl, /*!< in/out: tuple to write to */
ulint col_no, /*!< in: column number */
const void* value, /*!< in: integer value */
ulint value_len) /*!< in: sizeof value type */
{
const dfield_t* dfield;
ulint data_len;
ulint type_len;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
ut_a(col_no < ib_tuple_get_n_cols(ib_tpl));
dfield = ib_col_get_dfield(tuple, col_no);
data_len = dfield_get_len(dfield);
type_len = dtype_get_len(dfield_get_type(dfield));
if (dtype_get_mtype(dfield_get_type(dfield)) != DATA_INT
|| value_len != data_len) {
return(DB_DATA_MISMATCH);
}
return(ib_col_set_value(ib_tpl, col_no, value, type_len, true));
}
/*****************************************************************//**
Write an integer value to a column. Integers are stored in big-endian
format and will need to be converted from the host format.
@return DB_SUCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_write_i8(
/*==============*/
ib_tpl_t ib_tpl, /*!< in/out: tuple to write to */
int col_no, /*!< in: column number */
ib_i8_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
Write an integer value to a column. Integers are stored in big-endian
format and will need to be converted from the host format.
@return DB_SUCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_write_i16(
/*===============*/
ib_tpl_t ib_tpl, /*!< in/out: tuple to write to */
int col_no, /*!< in: column number */
ib_i16_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
Write an integer value to a column. Integers are stored in big-endian
format and will need to be converted from the host format.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_write_i32(
/*===============*/
ib_tpl_t ib_tpl, /*!< in/out: tuple to write to */
int col_no, /*!< in: column number */
ib_i32_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
Write an integer value to a column. Integers are stored in big-endian
format and will need to be converted from the host format.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_write_i64(
/*===============*/
ib_tpl_t ib_tpl, /*!< in/out: tuple to write to */
int col_no, /*!< in: column number */
ib_i64_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
Write an integer value to a column. Integers are stored in big-endian
format and will need to be converted from the host format.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_write_u8(
/*==============*/
ib_tpl_t ib_tpl, /*!< in/out: tuple to write to */
int col_no, /*!< in: column number */
ib_u8_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
Write an integer value to a column. Integers are stored in big-endian
format and will need to be converted from the host format.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_write_u16(
/*===============*/
ib_tpl_t ib_tpl, /*!< in/out: tupe to write to */
int col_no, /*!< in: column number */
ib_u16_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
Write an integer value to a column. Integers are stored in big-endian
format and will need to be converted from the host format.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_write_u32(
/*===============*/
ib_tpl_t ib_tpl, /*!< in/out: tuple to write to */
int col_no, /*!< in: column number */
ib_u32_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
Write an integer value to a column. Integers are stored in big-endian
format and will need to be converted from the host format.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_write_u64(
/*===============*/
ib_tpl_t ib_tpl, /*!< in/out: tuple to write to */
int col_no, /*!< in: column number */
ib_u64_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
Inform the cursor that it's the start of an SQL statement. */
UNIV_INTERN
void
ib_cursor_stmt_begin(
/*=================*/
ib_crsr_t ib_crsr) /*!< in: cursor */
{
ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr;
cursor->prebuilt->sql_stat_start = TRUE;
}
/*****************************************************************//**
Write a double value to a column.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_write_double(
/*==================*/
ib_tpl_t ib_tpl, /*!< in/out: tuple to write to */
int col_no, /*!< in: column number */
double val) /*!< in: value to write */
{
const dfield_t* dfield;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
dfield = ib_col_get_dfield(tuple, col_no);
if (dtype_get_mtype(dfield_get_type(dfield)) == DATA_DOUBLE) {
return(ib_col_set_value(ib_tpl, col_no,
&val, sizeof(val), true));
} else {
return(DB_DATA_MISMATCH);
}
}
/*************************************************************//**
Read a double column value from an InnoDB tuple.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_read_double(
/*=================*/
ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */
ib_ulint_t col_no, /*!< in: column number */
double* dval) /*!< out: double value */
{
ib_err_t err;
const dfield_t* dfield;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
dfield = ib_col_get_dfield(tuple, col_no);
if (dtype_get_mtype(dfield_get_type(dfield)) == DATA_DOUBLE) {
ib_col_copy_value_low(ib_tpl, col_no, dval, sizeof(*dval));
err = DB_SUCCESS;
} else {
err = DB_DATA_MISMATCH;
}
return(err);
}
/*****************************************************************//**
Write a float value to a column.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_write_float(
/*=================*/
ib_tpl_t ib_tpl, /*!< in/out: tuple to write to */
int col_no, /*!< in: column number */
float val) /*!< in: value to write */
{
const dfield_t* dfield;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
dfield = ib_col_get_dfield(tuple, col_no);
if (dtype_get_mtype(dfield_get_type(dfield)) == DATA_FLOAT) {
return(ib_col_set_value(ib_tpl, col_no,
&val, sizeof(val), true));
} else {
return(DB_DATA_MISMATCH);
}
}
/*************************************************************//**
Read a float value from an InnoDB tuple.
@return DB_SUCCESS or error */
UNIV_INTERN
ib_err_t
ib_tuple_read_float(
/*================*/
ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */
ib_ulint_t col_no, /*!< in: column number */
float* fval) /*!< out: float value */
{
ib_err_t err;
const dfield_t* dfield;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
dfield = ib_col_get_dfield(tuple, col_no);
if (dtype_get_mtype(dfield_get_type(dfield)) == DATA_FLOAT) {
ib_col_copy_value_low(ib_tpl, col_no, fval, sizeof(*fval));
err = DB_SUCCESS;
} else {
err = DB_DATA_MISMATCH;
}
return(err);
}
/*****************************************************************//**
Truncate a table. The cursor handle will be closed and set to NULL
on success.
@return DB_SUCCESS or error code */
UNIV_INTERN
ib_err_t
ib_cursor_truncate(
/*===============*/
ib_crsr_t* ib_crsr, /*!< in/out: cursor for table
to truncate */
ib_id_u64_t* table_id) /*!< out: new table id */
{
ib_err_t err;
ib_cursor_t* cursor = *(ib_cursor_t**) ib_crsr;
row_prebuilt_t* prebuilt = cursor->prebuilt;
*table_id = 0;
err = ib_cursor_lock(*ib_crsr, IB_LOCK_X);
if (err == DB_SUCCESS) {
trx_t* trx;
dict_table_t* table = prebuilt->table;
/* We are going to free the cursor and the prebuilt. Store
the transaction handle locally. */
trx = prebuilt->trx;
err = ib_cursor_close(*ib_crsr);
ut_a(err == DB_SUCCESS);
*ib_crsr = NULL;
/* A temp go around for assertion in trx_start_for_ddl_low
we already start the trx */
if (trx->state == TRX_STATE_ACTIVE) {
#ifdef UNIV_DEBUG
trx->start_file = 0;
#endif /* UNIV_DEBUG */
trx->dict_operation = TRX_DICT_OP_TABLE;
}
/* This function currently commits the transaction
on success. */
err = static_cast<ib_err_t>(
row_truncate_table_for_mysql(table, trx));
if (err == DB_SUCCESS) {
*table_id = (table->id);
}
}
return(err);
}
/*****************************************************************//**
Truncate a table.
@return DB_SUCCESS or error code */
UNIV_INTERN
ib_err_t
ib_table_truncate(
/*==============*/
const char* table_name, /*!< in: table name */
ib_id_u64_t* table_id) /*!< out: new table id */
{
ib_err_t err;
dict_table_t* table;
ib_err_t trunc_err;
ib_trx_t ib_trx = NULL;
ib_crsr_t ib_crsr = NULL;
ib_ulint_t memcached_sync = 0;
ib_trx = ib_trx_begin(IB_TRX_SERIALIZABLE, true, false);
dict_mutex_enter_for_mysql();
table = dict_table_open_on_name(table_name, TRUE, FALSE,
DICT_ERR_IGNORE_NONE);
if (table != NULL && dict_table_get_first_index(table)) {
err = ib_create_cursor_with_index_id(&ib_crsr, table, 0,
(trx_t*) ib_trx);
} else {
err = DB_TABLE_NOT_FOUND;
}
/* Remember the memcached_sync_count and set it to 0, so the
truncate can be executed. */
if (table != NULL && err == DB_SUCCESS) {
memcached_sync = table->memcached_sync_count;
table->memcached_sync_count = 0;
}
dict_mutex_exit_for_mysql();
if (err == DB_SUCCESS) {
trunc_err = ib_cursor_truncate(&ib_crsr, table_id);
ut_a(err == DB_SUCCESS);
} else {
trunc_err = err;
}
if (ib_crsr != NULL) {
err = ib_cursor_close(ib_crsr);
ut_a(err == DB_SUCCESS);
}
if (trunc_err == DB_SUCCESS) {
ut_a(ib_trx_state(ib_trx) == static_cast<ib_trx_state_t>(
TRX_STATE_NOT_STARTED));
err = ib_trx_release(ib_trx);
ut_a(err == DB_SUCCESS);
} else {
err = ib_trx_rollback(ib_trx);
ut_a(err == DB_SUCCESS);
}
/* Set the memcached_sync_count back. */
if (table != NULL && memcached_sync != 0) {
dict_mutex_enter_for_mysql();
table->memcached_sync_count = memcached_sync;
dict_mutex_exit_for_mysql();
}
return(trunc_err);
}
/*****************************************************************//**
Frees a possible InnoDB trx object associated with the current THD.
@return 0 or error number */
UNIV_INTERN
ib_err_t
ib_close_thd(
/*=========*/
void* thd) /*!< in: handle to the MySQL thread of the user
whose resources should be free'd */
{
innobase_close_thd(static_cast<THD*>(thd));
return(DB_SUCCESS);
}
/*****************************************************************//**
Return isolation configuration set by "innodb_api_trx_level"
@return trx isolation level*/
UNIV_INTERN
ib_trx_state_t
ib_cfg_trx_level()
/*==============*/
{
return(static_cast<ib_trx_state_t>(ib_trx_level_setting));
}
/*****************************************************************//**
Return configure value for background commit interval (in seconds)
@return background commit interval (in seconds) */
UNIV_INTERN
ib_ulint_t
ib_cfg_bk_commit_interval()
/*=======================*/
{
return(static_cast<ib_ulint_t>(ib_bk_commit_interval));
}
/*****************************************************************//**
Get generic configure status
@return configure status*/
UNIV_INTERN
int
ib_cfg_get_cfg()
/*============*/
{
int cfg_status;
cfg_status = (ib_binlog_enabled) ? IB_CFG_BINLOG_ENABLED : 0;
if (ib_mdl_enabled) {
cfg_status |= IB_CFG_MDL_ENABLED;
}
if (ib_disable_row_lock) {
cfg_status |= IB_CFG_DISABLE_ROWLOCK;
}
return(cfg_status);
}
/*****************************************************************//**
Increase/decrease the memcached sync count of table to sync memcached
DML with SQL DDLs.
@return DB_SUCCESS or error number */
UNIV_INTERN
ib_err_t
ib_cursor_set_memcached_sync(
/*=========================*/
ib_crsr_t ib_crsr, /*!< in: cursor */
ib_bool_t flag) /*!< in: true for increase */
{
const ib_cursor_t* cursor = (const ib_cursor_t*) ib_crsr;
row_prebuilt_t* prebuilt = cursor->prebuilt;
dict_table_t* table = prebuilt->table;
ib_err_t err = DB_SUCCESS;
if (table != NULL) {
/* If memcached_sync_count is -1, means table is
doing DDL, we just return error. */
if (table->memcached_sync_count == DICT_TABLE_IN_DDL) {
return(DB_ERROR);
}
if (flag) {
#ifdef HAVE_ATOMIC_BUILTINS
os_atomic_increment_lint(&table->memcached_sync_count, 1);
#else
dict_mutex_enter_for_mysql();
++table->memcached_sync_count;
dict_mutex_exit_for_mysql();
#endif
} else {
#ifdef HAVE_ATOMIC_BUILTINS
os_atomic_decrement_lint(&table->memcached_sync_count, 1);
#else
dict_mutex_enter_for_mysql();
--table->memcached_sync_count;
dict_mutex_exit_for_mysql();
#endif
ut_a(table->memcached_sync_count >= 0);
}
} else {
err = DB_TABLE_NOT_FOUND;
}
return(err);
}