Commit f0ddf5a6 authored by Rich Prohaska's avatar Rich Prohaska

support upsert x=x+values(x) expressions

parent 2090a320
set default_storage_engine='tokudb';
drop table if exists t;
set tokudb_disable_slow_upsert=1;
create table t (id int primary key, x int not null);
insert noar into t values (1,1);
insert noar into t values (1,1) on duplicate key update x=x+1;
select * from t;
id x
1 2
insert noar into t values (1,10) on duplicate key update x=values(x)+1;
ERROR 42000: Table 't' uses an extension that doesn't exist in this XYZ version
select * from t;
id x
1 2
insert noar into t values (1,10) on duplicate key update x=x+values(x);
select * from t;
id x
1 12
insert noar into t values (1,100) on duplicate key update x=x+values(x);
select * from t;
id x
1 112
drop table t;
# verify that values(x) works in update expression
source include/have_tokudb.inc;
set default_storage_engine='tokudb';
disable_warnings;
drop table if exists t;
enable_warnings;
set tokudb_disable_slow_upsert=1;
create table t (id int primary key, x int not null);
insert noar into t values (1,1);
insert noar into t values (1,1) on duplicate key update x=x+1;
select * from t;
replace_regex /MariaDB/XYZ/ /MySQL/XYZ/;
error ER_UNSUPPORTED_EXTENSION;
insert noar into t values (1,10) on duplicate key update x=values(x)+1;
select * from t;
insert noar into t values (1,10) on duplicate key update x=x+values(x);
select * from t;
insert noar into t values (1,100) on duplicate key update x=x+values(x);
select * from t;
drop table t;
......@@ -5,7 +5,6 @@
// Restrictions:
// No triggers
// Statement or mixed replication
// Does not support row based replication
// Primary key must be defined
// Simple and compound primary key
// Int, char and varchar primary key types
......@@ -14,10 +13,10 @@
// Integer and char field updates
// Update expressions:
// x = constant
// x = x+constant
// x = x-constant
// x = if(x=0,0,x-1)
// Session variable enables fast updates and fast upserts
// x = x + constant
// x = x - constant
// x = if (x=0,0,x-1)
// x = x + values(x)
// Session variable disables slow updates and slow upserts
// Future features:
......@@ -72,7 +71,14 @@ static void dump_item(Item *item) {
fprintf(stderr, ")\n");
break;
}
case Item::INSERT_VALUE_ITEM: {
Item_insert_value *value_item = static_cast<Item_insert_value*>(item);
fprintf(stderr, ":insert_value");
dump_item(value_item->arg);
break;
}
default:
fprintf(stderr, ":unsupported\n");
break;
}
}
......@@ -158,7 +164,6 @@ static uint32_t blob_field_index(TABLE *table, KEY_AND_COL_INFO *kc_info, uint i
int ha_tokudb::fast_update(THD *thd, List<Item> &update_fields, List<Item> &update_values, Item *conds) {
TOKUDB_DBUG_ENTER("ha_tokudb::fast_update");
int error = 0;
unsigned line = 0; // debug
if (tokudb_debug & TOKUDB_DEBUG_UPSERT) {
dump_item_list("fields", update_fields);
......@@ -170,24 +175,20 @@ int ha_tokudb::fast_update(THD *thd, List<Item> &update_fields, List<Item> &upda
if (update_fields.elements < 1 || update_fields.elements != update_values.elements) {
error = ENOTSUP; // something is fishy with the parameters
line = __LINE__;
goto return_error;
}
if (!check_fast_update(thd, update_fields, update_values, conds)) {
error = ENOTSUP;
line = __LINE__;
goto check_error;
}
error = send_update_message(update_fields, update_values, conds, transaction);
if (error != 0) {
line = __LINE__;
goto check_error;
}
check_error:
line = line; // debug
if (error != 0) {
if (get_disable_slow_update(thd))
error = HA_ERR_UNSUPPORTED;
......@@ -202,9 +203,9 @@ return_error:
// Return true if an expression is a simple int expression or a simple function of +- int expression.
static bool check_int_result(Item *item) {
Item::Type t = item->type();
if (t == Item::INT_ITEM)
if (t == Item::INT_ITEM) {
return true;
else if (t == Item::FUNC_ITEM) {
} else if (t == Item::FUNC_ITEM) {
Item_func *item_func = static_cast<Item_func*>(item);
if (strcmp(item_func->func_name(), "+") != 0 && strcmp(item_func->func_name(), "-") != 0)
return false;
......@@ -218,8 +219,21 @@ static bool check_int_result(Item *item) {
return false;
}
// check that an item is an insert value item with the same field name
static bool check_insert_value(Item *item, const char *field_name) {
if (item->type() != Item::INSERT_VALUE_ITEM)
return false;
Item_insert_value *value_item = static_cast<Item_insert_value*>(item);
if (value_item->arg->type() != Item::FIELD_ITEM)
return false;
Item_field *arg = static_cast<Item_field*>(value_item->arg);
if (strcmp(field_name, arg->field_name) != 0)
return false;
return true;
}
// Return true if an expression looks like field_name op constant.
static bool check_x_op_constant(const char *field_name, Item *item, const char *op, Item **item_constant) {
static bool check_x_op_constant(const char *field_name, Item *item, const char *op, Item **item_constant, bool allow_insert_value) {
if (item->type() != Item::FUNC_ITEM)
return false;
Item_func *item_func = static_cast<Item_func*>(item);
......@@ -235,7 +249,8 @@ static bool check_x_op_constant(const char *field_name, Item *item, const char *
if (strcmp(field_name, arg0->field_name) != 0)
return false;
if (!check_int_result(arguments[1]))
return false;
if (!(allow_insert_value && check_insert_value(arguments[1], field_name)))
return false;
*item_constant = arguments[1];
return true;
}
......@@ -243,9 +258,9 @@ static bool check_x_op_constant(const char *field_name, Item *item, const char *
// Return true if an expression looks like field_name = constant
static bool check_x_equal_0(const char *field_name, Item *item) {
Item *item_constant;
if (!check_x_op_constant(field_name, item, "=", &item_constant))
if (!check_x_op_constant(field_name, item, "=", &item_constant, false))
return false;
if (item_constant->val_int() != 0)
if (item_constant->type() != Item::INT_ITEM || item_constant->val_int() != 0)
return false;
return true;
}
......@@ -253,9 +268,9 @@ static bool check_x_equal_0(const char *field_name, Item *item) {
// Return true if an expression looks like fieldname - 1
static bool check_x_minus_1(const char *field_name, Item *item) {
Item *item_constant;
if (!check_x_op_constant(field_name, item, "-", &item_constant))
if (!check_x_op_constant(field_name, item, "-", &item_constant, false))
return false;
if (item_constant->val_int() != 1)
if (item_constant->type() != Item::INT_ITEM || item_constant->val_int() != 1)
return false;
return true;
}
......@@ -284,7 +299,7 @@ static bool check_decr_floor_expression(Field *lhs_field, Item *item) {
}
// Check if lhs = rhs expression is simple. Return true if it is.
static bool check_update_expression(Item *lhs_item, Item *rhs_item, TABLE *table) {
static bool check_update_expression(Item *lhs_item, Item *rhs_item, TABLE *table, bool allow_insert_value) {
Field *lhs_field = find_field_by_name(table, lhs_item);
if (lhs_field == NULL)
return false;
......@@ -301,9 +316,9 @@ static bool check_update_expression(Item *lhs_item, Item *rhs_item, TABLE *table
if (check_int_result(rhs_item))
return true;
Item *item_constant;
if (check_x_op_constant(lhs_field->field_name, rhs_item, "+", &item_constant))
if (check_x_op_constant(lhs_field->field_name, rhs_item, "+", &item_constant, allow_insert_value))
return true;
if (check_x_op_constant(lhs_field->field_name, rhs_item, "-", &item_constant))
if (check_x_op_constant(lhs_field->field_name, rhs_item, "-", &item_constant, allow_insert_value))
return true;
if (check_decr_floor_expression(lhs_field, rhs_item))
return true;
......@@ -324,7 +339,7 @@ static bool check_update_expression(Item *lhs_item, Item *rhs_item, TABLE *table
}
// Check that all update expressions are simple. Return true if they are.
static bool check_all_update_expressions(List<Item> &fields, List<Item> &values, TABLE *table) {
static bool check_all_update_expressions(List<Item> &fields, List<Item> &values, TABLE *table, bool allow_insert_value) {
List_iterator<Item> lhs_i(fields);
List_iterator<Item> rhs_i(values);
while (1) {
......@@ -332,9 +347,8 @@ static bool check_all_update_expressions(List<Item> &fields, List<Item> &values,
if (lhs_item == NULL)
break;
Item *rhs_item = rhs_i++;
if (rhs_item == NULL)
assert(0); // can not happen
if (!check_update_expression(lhs_item, rhs_item, table))
assert(rhs_item != NULL);
if (!check_update_expression(lhs_item, rhs_item, table, allow_insert_value))
return false;
}
return true;
......@@ -477,7 +491,7 @@ bool ha_tokudb::check_fast_update(THD *thd, List<Item> &fields, List<Item> &valu
if (clustering_keys_exist(table))
return false;
if (!check_all_update_expressions(fields, values, table))
if (!check_all_update_expressions(fields, values, table, false))
return false;
if (!check_point_update(conds, table))
......@@ -508,7 +522,17 @@ static void marshall_blobs_descriptor(tokudb::buffer &b, TABLE *table, KEY_AND_C
static inline uint32_t get_null_bit_position(uint32_t null_bit);
// Marshall update operatins to a buffer.
// evaluate the int value of an item
static longlong item_val_int(Item *item) {
Item::Type t = item->type();
if (t == Item::INSERT_VALUE_ITEM) {
Item_insert_value *value_item = static_cast<Item_insert_value*>(item);
return value_item->arg->val_int();
} else
return item->val_int();
}
// Marshall update operations to a buffer.
static void marshall_update(tokudb::buffer &b, Item *lhs_item, Item *rhs_item, TABLE *table, TOKUDB_SHARE *share) {
// figure out the update operation type (again)
Field *lhs_field = find_field_by_name(table, lhs_item);
......@@ -549,14 +573,14 @@ static void marshall_update(tokudb::buffer &b, Item *lhs_item, Item *rhs_item, T
Item_func *rhs_func = static_cast<Item_func*>(rhs_item);
Item **arguments = rhs_func->arguments();
if (strcmp(rhs_func->func_name(), "if") == 0) {
update_operation = '-'; // we only support one if function for now, and it is a descrement with floor.
update_operation = '-'; // we only support one if function for now, and it is a decrement with floor.
v_ll = 1;
} else if (rhs_func->argument_count() == 1) {
update_operation = '=';
v_ll = rhs_func->val_int();
} else {
update_operation = rhs_func->func_name()[0];
v_ll = arguments[1]->val_int();
v_ll = item_val_int(arguments[1]);
}
v_length = lhs_field->pack_length();
v_ptr = &v_ll;
......@@ -717,8 +741,7 @@ int ha_tokudb::send_update_message(List<Item> &update_fields, List<Item> &update
if (lhs_item == NULL)
break;
Item *rhs_item = rhs_i++;
if (rhs_item == NULL)
assert(0); // can not happen
assert(rhs_item != NULL);
marshall_update(update_message, lhs_item, rhs_item, table, share);
}
......@@ -746,7 +769,6 @@ int ha_tokudb::upsert(THD *thd, List<Item> &update_fields, List<Item> &update_va
TOKUDB_DBUG_ENTER("ha_tokudb::upsert");
int error = 0;
unsigned line = 0; // debug
if (tokudb_debug & TOKUDB_DEBUG_UPSERT) {
fprintf(stderr, "upsert\n");
......@@ -756,24 +778,20 @@ int ha_tokudb::upsert(THD *thd, List<Item> &update_fields, List<Item> &update_va
if (update_fields.elements < 1 || update_fields.elements != update_values.elements) {
error = ENOTSUP; // not an upsert or something is fishy with the parameters
line = __LINE__;
goto return_error;
}
if (!check_upsert(thd, update_fields, update_values)) {
error = ENOTSUP;
line = __LINE__;
goto check_error;
}
error = send_upsert_message(thd, update_fields, update_values, transaction);
if (error != 0) {
line = __LINE__;
goto check_error;
}
check_error:
line = line; // debug
if (error != 0) {
if (get_disable_slow_upsert(thd))
error = HA_ERR_UNSUPPORTED;
......@@ -811,7 +829,7 @@ bool ha_tokudb::check_upsert(THD *thd, List<Item> &update_fields, List<Item> &up
!(thd->variables.binlog_format == BINLOG_FORMAT_STMT || thd->variables.binlog_format == BINLOG_FORMAT_MIXED))
return false;
if (!check_all_update_expressions(update_fields, update_values, table))
if (!check_all_update_expressions(update_fields, update_values, table, true))
return false;
return true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment