polardbxengine/sql/table_ext.cc

420 lines
11 KiB
C++

/* Copyright (c) 2018, 2021, Alibaba 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, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL/PolarDB-X Engine hereby grant you an
additional permission to link the program and your derivative works with the
separately licensed software that they have included with
MySQL/PolarDB-X Engine.
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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "sql/table_ext.h"
#include "sql/item.h"
#include "sql/sql_class.h"
#include "sql/sql_lex.h"
#include "sql/item_timefunc.h"
namespace im {
/* Cast text or decimal to integer */
class Item_typecast_cnum : public Item_int_func {
public:
Item_typecast_cnum(Item *a) : Item_int_func(a) {}
enum Functype functype() const { return TYPECAST_FUNC; }
uint decimal_precision() const { return args[0]->decimal_precision(); }
bool resolve_type(THD *) {
fix_char_length(std::min<uint32>(args[0]->max_char_length(),
MY_INT64_NUM_DECIMAL_DIGITS));
return reject_geometry_args(arg_count, args, this);
}
longlong val_int() {
longlong value;
int error;
size_t length;
const char *end;
const CHARSET_INFO *cs;
char buff[MAX_FIELD_WIDTH], *start;
String tmp(buff, sizeof(buff), &my_charset_bin), *res;
Item_result result_type = args[0]->cast_to_int_type();
if (result_type == DECIMAL_RESULT) {
value = args[0]->val_int();
null_value = args[0]->null_value;
return value;
}
DBUG_ASSERT(result_type == STRING_RESULT);
/* null value */
if (!(res = args[0]->val_str(&tmp))) {
null_value = 1;
goto fail;
}
null_value = 0;
start = res->ptr();
length = res->length();
cs = res->charset();
end = start + length;
value = cs->cset->strtoll10(cs, start, &end, &error);
if (error > 0 || end != start + length) goto fail;
return value;
fail:
my_error(ER_AS_OF_BAD_SCN_TYPE, MYF(0));
return 0;
}
};
class Item_typecast_scn : public Item_typecast_cnum {
public:
Item_typecast_scn(Item *a) : Item_typecast_cnum(a) {}
const char *func_name() const { return "cast_as_scn"; }
void print(const THD *thd, String *str, enum_query_type query_type) const {
str->append(STRING_WITH_LEN("cast("));
args[0]->print(thd, str, query_type);
str->append(STRING_WITH_LEN(" as scn)"));
}
};
class Item_typecast_gcn : public Item_typecast_cnum {
public:
Item_typecast_gcn(Item *a) : Item_typecast_cnum(a) {}
const char *func_name() const { return "cast_as_gcn"; }
void print(const THD *thd, String *str, enum_query_type query_type) const {
str->append(STRING_WITH_LEN("cast("));
args[0]->print(thd, str, query_type);
str->append(STRING_WITH_LEN(" as gcn)"));
}
};
/* Itemize the snapshot items */
bool Table_snapshot::itemize(Parse_context *pc, TABLE_LIST *owner) {
DBUG_ASSERT(valid());
DBUG_ASSERT(pc && pc->thd && pc->thd->lex);
DBUG_ASSERT(owner && !owner->snapshot_expr.is_set());
/* As of clause */
if (ts || scn || gcn) {
SELECT_LEX *select = owner->select_lex;
DBUG_ASSERT(select);
if (select->select_number == 1 && pc->thd->lex->is_update_stmt) {
my_error(ER_AS_OF_NOT_SELECT, MYF(0));
return true;
}
if (ts) {
if (ts->itemize(pc, &ts)) return true;
owner->snapshot_expr.ts = ts;
} else if (scn) {
if (scn->itemize(pc, &scn)) return true;
owner->snapshot_expr.scn = scn;
} else {
if (gcn->itemize(pc, &gcn)) return true;
owner->snapshot_expr.gcn = gcn;
}
/* Disable query cache if snapshot expression is set. */
pc->thd->lex->safe_to_cache_query = false;
}
return false;
}
static bool is_string_item(Item *item) {
switch (item->data_type()) {
case MYSQL_TYPE_VARCHAR:
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_VAR_STRING:
case MYSQL_TYPE_STRING:
return true;
default:
return false;
}
}
static bool is_decimal_zero(Item *item) {
switch (item->data_type()) {
case MYSQL_TYPE_DECIMAL:
case MYSQL_TYPE_NEWDECIMAL:
return (item->decimals == 0);
default:
return false;
}
}
static bool try_cast_to_datetime(THD *thd, Item **item) {
/* We only try to cast string item to datetime.
If it cannot cast, errors will be raised when evaluating */
if (!is_string_item(*item)) return true;
Item *cast = new Item_typecast_datetime(*item);
if (!cast) return true;
cast->fix_fields(thd, item);
thd->change_item_tree(item, cast);
return false;
}
static bool try_cast_to_scn(THD *thd, Item **item) {
/* We only try to cast string or decimal item to scn.
If it cannot cast, errors will be raised when evaluating */
if (!is_string_item(*item) && !is_decimal_zero(*item)) return true;
Item *cast = new Item_typecast_scn(*item);
if (!cast) return true;
cast->fix_fields(thd, item);
thd->change_item_tree(item, cast);
return false;
}
static bool try_cast_to_gcn(THD *thd, Item **item) {
/* We only try to cast string or decimal item to scn.
If it cannot cast, errors will be raised when evaluating */
if (!is_string_item(*item) && !is_decimal_zero(*item)) return true;
Item *cast = new Item_typecast_gcn(*item);
if (!cast) return true;
cast->fix_fields(thd, item);
thd->change_item_tree(item, cast);
return false;
}
static bool fix_and_check_const(THD *thd, Item **item) {
if (!(*item)->fixed && (*item)->fix_fields(thd, item)) return true;
if (!(*item)->const_for_execution()) {
my_error(ER_AS_OF_EXPR_NOT_CONSTANT, MYF(0));
return true;
}
return false;
}
/* Fix fields, and make sure expr is constant */
bool Table_snapshot::fix_fields(THD *thd) {
DBUG_ASSERT(valid());
if (ts) {
if (fix_and_check_const(thd, &ts)) return true;
if (!ts->is_temporal_with_date_and_time()) {
if (try_cast_to_datetime(thd, &ts)) {
my_error(ER_AS_OF_BAD_TIMESTAMP_TYPE, MYF(0));
return true;
}
}
} else if (scn) {
if (fix_and_check_const(thd, &scn)) return true;
if (!is_integer_type(scn->data_type())) {
if (try_cast_to_scn(thd, &scn)) {
my_error(ER_AS_OF_BAD_SCN_TYPE, MYF(0));
return true;
}
}
} else if (gcn) {
if (fix_and_check_const(thd, &gcn)) return true;
if (!is_integer_type(gcn->data_type())) {
if (try_cast_to_gcn(thd, &gcn)) {
my_error(ER_AS_OF_BAD_SCN_TYPE, MYF(0));
return true;
}
}
}
return false;
}
/**
Evaluate timestamp value.
@param ts Timestamp item
@param out UTC value caculated
@return true Some error occurs.
*/
bool Table_snapshot::evaluate_timestamp(Item *ts, uint64_t *out) {
DBUG_ASSERT(ts && out);
DBUG_ASSERT(current_thd);
int unused;
struct timeval tm;
if (ts->get_timeval(&tm, &unused)) {
my_error(ER_AS_OF_BAD_TIMESTAMP_TYPE, MYF(0));
return true;
}
if (current_thd->is_error()) return true;
*out = tm.tv_sec;
return false;
}
/**
Evaluate timestamp value.
@param scn SCN item
@param out SCN value caculated
@return true Some error occurs.
*/
bool Table_snapshot::evaluate_scn(Item *scn, uint64_t *out) {
DBUG_ASSERT(scn && out);
DBUG_ASSERT(current_thd);
uint64_t val = scn->val_uint();
if (current_thd->is_error()) return true;
*out = val;
return false;
}
bool Table_snapshot::evaluate_gcn(Item *gcn, uint64_t *out) {
DBUG_ASSERT(gcn && out);
DBUG_ASSERT(current_thd);
uint64_t val = gcn->val_uint();
if (current_thd->is_error()) return true;
*out = val;
return false;
}
/**
Evaluate snapshot value.
@param snapshot Snap value caculated
@return true Some error occurs.
*/
bool Table_snapshot::evaluate(Snapshot_info_t *snapshot) {
if (!is_set()) return false;
uint64_t val;
DBUG_ASSERT(valid());
if (ts) {
if (evaluate_timestamp(ts, &val)) return true;
snapshot->set_timestamp(val);
} else if (scn) {
if (evaluate_scn(scn, &val)) return true;
snapshot->set_scn(val);
} else if (gcn) {
if (evaluate_gcn(gcn, &val)) return true;
snapshot->set_gcn(val);
}
return false;
}
/*
Reset snapshot, increase the snapshot table count.
*/
void init_table_snapshot(TABLE *table, THD *thd) {
DBUG_ASSERT(thd && thd->lex);
table->snapshot.reset();
if (table->pos_in_table_list->snapshot_expr.is_set())
thd->lex->table_snap_expr_count_to_evaluate++;
}
/*
Evaluate table snapshot expressions.
@return true If some error occurs.
*/
bool evaluate_snapshot(THD *thd, const LEX *lex) {
DBUG_ASSERT(thd->lex->table_snap_expr_count_to_evaluate >= 0);
/* Cases that need not do evaluating */
if ((thd->lex->table_snap_expr_count_to_evaluate == 0) || /* No snapshot */
(lex->is_explain() && !lex->is_explain_analyze)) /* Not analyze */
return false;
DBUG_ASSERT(thd->open_tables);
for (TABLE *table = thd->open_tables; table; table = table->next) {
DBUG_ASSERT(table->pos_in_table_list);
DBUG_ASSERT(!table->snapshot.valid());
if (table->pos_in_table_list->snapshot_expr.evaluate(&table->snapshot))
return true;
set_update_snapshot_gcn_if_needed(thd, &table->snapshot);
}
return false;
}
/**
Simulate asof syntax by adding Item onto Table_snapshot.
@param[in] thd current context
@param[in/out] snapshot ASOF attributes
*/
void simulate_snapshot_clause(THD *thd, TABLE_LIST *all_tables) {
Item *item = nullptr;
ulonglong gcn = thd->variables.innodb_snapshot_gcn;
/**
* The value is max gcn + 1 if innodb_current_snapshot_gcn is setted.
* It will make sure lastest record can be seen by current gcn.
*/
if (gcn == MYSQL_GCN_NULL && thd->variables.innodb_current_snapshot_gcn) {
if(!ha_acquire_gcn((uint64 *)&gcn)) gcn = gcn + 1;
}
if (gcn != MYSQL_GCN_NULL) {
TABLE_LIST *table;
for (table = all_tables; table; table = table->next_global) {
if (!table->snapshot_expr.is_set()) {
item = new (thd->mem_root) Item_int(gcn);
table->snapshot_expr.gcn = item;
}
}
}
}
void set_update_snapshot_gcn_if_needed(THD *thd, Snapshot_info_t *snapshot_info) {
if (!thd || !snapshot_info) return;
if (snapshot_info->get_type() != AS_OF_GCN) return;
DBUG_ASSERT(snapshot_info->get_update_snapshot_gcn());
if (thd->variables.innodb_snapshot_gcn == MYSQL_GCN_NULL &&
thd->variables.innodb_current_snapshot_gcn == true) {
snapshot_info->set_update_snapshot_gcn(false);
}
}
} // namespace im