mariadb/storage/mroonga/lib/mrn_condition_converter.cpp

609 lines
18 KiB
C++
Raw Normal View History

/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2013-2014 Kouhei Sutou <kou@clear-code.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "mrn_condition_converter.hpp"
#include "mrn_time_converter.hpp"
#include "mrn_smart_grn_obj.hpp"
// for debug
#define MRN_CLASS_NAME "mrn::ConditionConverter"
#ifdef MRN_ITEM_HAVE_ITEM_NAME
# define MRN_ITEM_FIELD_GET_NAME(item) ((item)->item_name.ptr())
# define MRN_ITEM_FIELD_GET_NAME_LENGTH(item) ((item)->item_name.length())
#else
# define MRN_ITEM_FIELD_GET_NAME(item) ((item)->name)
# define MRN_ITEM_FIELD_GET_NAME_LENGTH(item) (strlen((item)->name))
#endif
namespace mrn {
ConditionConverter::ConditionConverter(grn_ctx *ctx, grn_obj *table,
bool is_storage_mode)
: ctx_(ctx),
table_(table),
is_storage_mode_(is_storage_mode) {
GRN_TEXT_INIT(&column_name_, 0);
GRN_VOID_INIT(&value_);
}
ConditionConverter::~ConditionConverter() {
grn_obj_unlink(ctx_, &column_name_);
grn_obj_unlink(ctx_, &value_);
}
bool ConditionConverter::is_convertable(const Item *item) {
MRN_DBUG_ENTER_METHOD();
if (!item) {
DBUG_RETURN(false);
}
switch (item->type()) {
case Item::COND_ITEM:
{
const Item_cond *cond_item = reinterpret_cast<const Item_cond *>(item);
bool convertable = is_convertable(cond_item);
DBUG_RETURN(convertable);
}
break;
case Item::FUNC_ITEM:
{
const Item_func *func_item = reinterpret_cast<const Item_func *>(item);
bool convertable = is_convertable(func_item);
DBUG_RETURN(convertable);
}
break;
default:
DBUG_RETURN(false);
break;
}
DBUG_RETURN(false);
}
bool ConditionConverter::is_convertable(const Item_cond *cond_item) {
MRN_DBUG_ENTER_METHOD();
if (!is_storage_mode_) {
DBUG_RETURN(false);
}
if (cond_item->functype() != Item_func::COND_AND_FUNC) {
DBUG_RETURN(false);
}
List<Item> *argument_list =
const_cast<Item_cond *>(cond_item)->argument_list();
List_iterator<Item> iterator(*argument_list);
const Item *sub_item;
while ((sub_item = iterator++)) {
if (!is_convertable(sub_item)) {
DBUG_RETURN(false);
}
}
DBUG_RETURN(true);
}
bool ConditionConverter::is_convertable(const Item_func *func_item) {
MRN_DBUG_ENTER_METHOD();
switch (func_item->functype()) {
case Item_func::EQ_FUNC:
case Item_func::LT_FUNC:
case Item_func::LE_FUNC:
case Item_func::GE_FUNC:
case Item_func::GT_FUNC:
if (!is_storage_mode_) {
DBUG_RETURN(false);
}
{
Item **arguments = func_item->arguments();
Item *left_item = arguments[0];
Item *right_item = arguments[1];
if (left_item->type() != Item::FIELD_ITEM) {
DBUG_RETURN(false);
}
if (!right_item->basic_const_item()) {
DBUG_RETURN(false);
}
bool convertable =
is_convertable_binary_operation(static_cast<Item_field *>(left_item),
right_item,
func_item->functype());
DBUG_RETURN(convertable);
}
break;
case Item_func::FT_FUNC:
DBUG_RETURN(true);
break;
case Item_func::BETWEEN:
if (!is_storage_mode_) {
DBUG_RETURN(false);
}
{
Item **arguments = func_item->arguments();
Item *target_item = arguments[0];
Item *min_item = arguments[1];
Item *max_item = arguments[2];
if (target_item->type() != Item::FIELD_ITEM) {
DBUG_RETURN(false);
}
if (!min_item->basic_const_item()) {
DBUG_RETURN(false);
}
if (!max_item->basic_const_item()) {
DBUG_RETURN(false);
}
bool convertable =
is_convertable_between(static_cast<Item_field *>(target_item),
min_item,
max_item);
DBUG_RETURN(convertable);
}
default:
DBUG_RETURN(false);
break;
}
DBUG_RETURN(true);
}
bool ConditionConverter::is_convertable_binary_operation(
const Item_field *field_item,
Item *value_item,
Item_func::Functype func_type) {
MRN_DBUG_ENTER_METHOD();
bool convertable = false;
enum_field_types field_type = field_item->field_type();
NormalizedType normalized_type = normalize_field_type(field_type);
switch (normalized_type) {
case STRING_TYPE:
if (value_item->type() == Item::STRING_ITEM &&
func_type == Item_func::EQ_FUNC) {
convertable = have_index(field_item, GRN_OP_EQUAL);
}
break;
case INT_TYPE:
convertable = value_item->type() == Item::INT_ITEM;
break;
case TIME_TYPE:
if (is_valid_time_value(field_item, value_item)) {
convertable = have_index(field_item, func_type);
}
break;
case UNSUPPORTED_TYPE:
break;
}
DBUG_RETURN(convertable);
}
bool ConditionConverter::is_convertable_between(const Item_field *field_item,
Item *min_item,
Item *max_item) {
MRN_DBUG_ENTER_METHOD();
bool convertable = false;
enum_field_types field_type = field_item->field_type();
NormalizedType normalized_type = normalize_field_type(field_type);
switch (normalized_type) {
case STRING_TYPE:
if (min_item->type() == Item::STRING_ITEM &&
max_item->type() == Item::STRING_ITEM) {
convertable = have_index(field_item, GRN_OP_LESS);
}
break;
case INT_TYPE:
if (min_item->type() == Item::INT_ITEM &&
max_item->type() == Item::INT_ITEM) {
convertable = have_index(field_item, GRN_OP_LESS);
}
break;
case TIME_TYPE:
if (is_valid_time_value(field_item, min_item) &&
is_valid_time_value(field_item, max_item)) {
convertable = have_index(field_item, GRN_OP_LESS);
}
break;
case UNSUPPORTED_TYPE:
break;
}
DBUG_RETURN(convertable);
}
bool ConditionConverter::is_valid_time_value(const Item_field *field_item,
Item *value_item) {
MRN_DBUG_ENTER_METHOD();
MYSQL_TIME mysql_time;
bool error = get_time_value(field_item, value_item, &mysql_time);
DBUG_RETURN(!error);
}
bool ConditionConverter::get_time_value(const Item_field *field_item,
Item *value_item,
MYSQL_TIME *mysql_time) {
MRN_DBUG_ENTER_METHOD();
bool error;
Item *real_value_item = value_item->real_item();
switch (field_item->field_type()) {
case MYSQL_TYPE_TIME:
error = real_value_item->get_time(mysql_time);
break;
case MYSQL_TYPE_YEAR:
mysql_time->year = static_cast<int>(value_item->val_int());
mysql_time->month = 1;
mysql_time->day = 1;
mysql_time->hour = 0;
mysql_time->hour = 0;
mysql_time->minute = 0;
mysql_time->second_part = 0;
mysql_time->neg = false;
mysql_time->time_type = MYSQL_TIMESTAMP_DATE;
error = false;
break;
default:
error = real_value_item->get_date(mysql_time, TIME_FUZZY_DATE);
break;
}
DBUG_RETURN(error);
}
ConditionConverter::NormalizedType
ConditionConverter::normalize_field_type(enum_field_types field_type) {
MRN_DBUG_ENTER_METHOD();
NormalizedType type = UNSUPPORTED_TYPE;
switch (field_type) {
case MYSQL_TYPE_DECIMAL:
type = STRING_TYPE;
break;
case MYSQL_TYPE_TINY:
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_LONG:
type = INT_TYPE;
break;
case MYSQL_TYPE_FLOAT:
case MYSQL_TYPE_DOUBLE:
type = UNSUPPORTED_TYPE;
break;
case MYSQL_TYPE_NULL:
type = UNSUPPORTED_TYPE;
break;
case MYSQL_TYPE_TIMESTAMP:
type = TIME_TYPE;
break;
case MYSQL_TYPE_LONGLONG:
case MYSQL_TYPE_INT24:
type = INT_TYPE;
break;
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_YEAR:
case MYSQL_TYPE_NEWDATE:
type = TIME_TYPE;
break;
case MYSQL_TYPE_VARCHAR:
type = STRING_TYPE;
break;
case MYSQL_TYPE_BIT:
type = INT_TYPE;
break;
#ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2
case MYSQL_TYPE_TIMESTAMP2:
type = TIME_TYPE;
break;
#endif
#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
case MYSQL_TYPE_DATETIME2:
type = TIME_TYPE;
break;
#endif
#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
case MYSQL_TYPE_TIME2:
type = TIME_TYPE;
break;
#endif
case MYSQL_TYPE_NEWDECIMAL:
type = STRING_TYPE;
break;
case MYSQL_TYPE_ENUM:
type = INT_TYPE;
break;
case MYSQL_TYPE_SET:
type = INT_TYPE;
break;
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:
type = STRING_TYPE;
break;
case MYSQL_TYPE_GEOMETRY:
type = UNSUPPORTED_TYPE;
break;
}
DBUG_RETURN(type);
}
bool ConditionConverter::have_index(const Item_field *field_item,
grn_operator _operator) {
MRN_DBUG_ENTER_METHOD();
grn_obj *column;
column = grn_obj_column(ctx_, table_,
MRN_ITEM_FIELD_GET_NAME(field_item),
MRN_ITEM_FIELD_GET_NAME_LENGTH(field_item));
if (!column) {
DBUG_RETURN(false);
}
mrn::SmartGrnObj smart_column(ctx_, column);
int n_indexes = grn_column_index(ctx_, column, _operator, NULL, 0, NULL);
bool convertable = (n_indexes > 0);
DBUG_RETURN(convertable);
}
bool ConditionConverter::have_index(const Item_field *field_item,
Item_func::Functype func_type) {
MRN_DBUG_ENTER_METHOD();
bool have = false;
switch (func_type) {
case Item_func::EQ_FUNC:
have = have_index(field_item, GRN_OP_EQUAL);
break;
case Item_func::LT_FUNC:
have = have_index(field_item, GRN_OP_LESS);
break;
case Item_func::LE_FUNC:
have = have_index(field_item, GRN_OP_LESS_EQUAL);
break;
case Item_func::GE_FUNC:
have = have_index(field_item, GRN_OP_GREATER_EQUAL);
break;
case Item_func::GT_FUNC:
have = have_index(field_item, GRN_OP_GREATER);
break;
default:
break;
}
DBUG_RETURN(have);
}
const Item_func *ConditionConverter::find_match_against(const Item *item) {
MRN_DBUG_ENTER_METHOD();
if (!item) {
DBUG_RETURN(NULL);
}
switch (item->type()) {
case Item::COND_ITEM:
if (is_storage_mode_) {
Item_cond *cond_item = (Item_cond *)item;
if (cond_item->functype() == Item_func::COND_AND_FUNC) {
List_iterator<Item> iterator(*((cond_item)->argument_list()));
const Item *sub_item;
while ((sub_item = iterator++)) {
const Item_func *match_against = find_match_against(sub_item);
if (match_against) {
DBUG_RETURN(match_against);
}
}
}
}
break;
case Item::FUNC_ITEM:
{
const Item_func *func_item = (const Item_func *)item;
switch (func_item->functype()) {
case Item_func::FT_FUNC:
DBUG_RETURN(func_item);
break;
default:
break;
}
}
break;
default:
break;
}
DBUG_RETURN(NULL);
}
void ConditionConverter::convert(const Item *where, grn_obj *expression) {
MRN_DBUG_ENTER_METHOD();
if (!where || where->type() != Item::COND_ITEM) {
DBUG_VOID_RETURN;
}
Item_cond *cond_item = (Item_cond *)where;
List_iterator<Item> iterator(*((cond_item)->argument_list()));
const Item *sub_item;
while ((sub_item = iterator++)) {
switch (sub_item->type()) {
case Item::FUNC_ITEM:
{
const Item_func *func_item = (const Item_func *)sub_item;
switch (func_item->functype()) {
case Item_func::EQ_FUNC:
convert_binary_operation(func_item, expression, GRN_OP_EQUAL);
break;
case Item_func::LT_FUNC:
convert_binary_operation(func_item, expression, GRN_OP_LESS);
break;
case Item_func::LE_FUNC:
convert_binary_operation(func_item, expression, GRN_OP_LESS_EQUAL);
break;
case Item_func::GE_FUNC:
convert_binary_operation(func_item, expression,
GRN_OP_GREATER_EQUAL);
break;
case Item_func::GT_FUNC:
convert_binary_operation(func_item, expression, GRN_OP_GREATER);
break;
case Item_func::BETWEEN:
convert_between(func_item, expression);
break;
default:
break;
}
}
break;
default:
break;
}
}
DBUG_VOID_RETURN;
}
void ConditionConverter::convert_binary_operation(const Item_func *func_item,
grn_obj *expression,
grn_operator _operator) {
Item **arguments = func_item->arguments();
Item *left_item = arguments[0];
Item *right_item = arguments[1];
if (left_item->type() == Item::FIELD_ITEM) {
const Item_field *field_item = static_cast<const Item_field *>(left_item);
append_field_value(field_item, expression);
append_const_item(field_item, right_item, expression);
grn_expr_append_op(ctx_, expression, _operator, 2);
grn_expr_append_op(ctx_, expression, GRN_OP_AND, 2);
}
}
void ConditionConverter::convert_between(const Item_func *func_item,
grn_obj *expression) {
MRN_DBUG_ENTER_METHOD();
Item **arguments = func_item->arguments();
Item *target_item = arguments[0];
Item *min_item = arguments[1];
Item *max_item = arguments[2];
grn_obj *between_func = grn_ctx_get(ctx_, "between", strlen("between"));
grn_expr_append_obj(ctx_, expression, between_func, GRN_OP_PUSH, 1);
const Item_field *field_item = static_cast<const Item_field *>(target_item);
append_field_value(field_item, expression);
grn_obj include;
mrn::SmartGrnObj smart_include(ctx_, &include);
GRN_TEXT_INIT(&include, 0);
GRN_TEXT_PUTS(ctx_, &include, "include");
append_const_item(field_item, min_item, expression);
grn_expr_append_const(ctx_, expression, &include, GRN_OP_PUSH, 1);
append_const_item(field_item, max_item, expression);
grn_expr_append_const(ctx_, expression, &include, GRN_OP_PUSH, 1);
grn_expr_append_op(ctx_, expression, GRN_OP_CALL, 5);
grn_expr_append_op(ctx_, expression, GRN_OP_AND, 2);
DBUG_VOID_RETURN;
}
void ConditionConverter::append_field_value(const Item_field *field_item,
grn_obj *expression) {
MRN_DBUG_ENTER_METHOD();
GRN_BULK_REWIND(&column_name_);
GRN_TEXT_PUT(ctx_, &column_name_,
MRN_ITEM_FIELD_GET_NAME(field_item),
MRN_ITEM_FIELD_GET_NAME_LENGTH(field_item));
grn_expr_append_const(ctx_, expression, &column_name_,
GRN_OP_PUSH, 1);
grn_expr_append_op(ctx_, expression, GRN_OP_GET_VALUE, 1);
DBUG_VOID_RETURN;
}
void ConditionConverter::append_const_item(const Item_field *field_item,
Item *const_item,
grn_obj *expression) {
MRN_DBUG_ENTER_METHOD();
enum_field_types field_type = field_item->field_type();
NormalizedType normalized_type = normalize_field_type(field_type);
switch (normalized_type) {
case STRING_TYPE:
grn_obj_reinit(ctx_, &value_, GRN_DB_TEXT, 0);
{
String *string;
string = const_item->val_str(NULL);
GRN_TEXT_SET(ctx_, &value_, string->ptr(), string->length());
}
break;
case INT_TYPE:
grn_obj_reinit(ctx_, &value_, GRN_DB_INT64, 0);
GRN_INT64_SET(ctx_, &value_, const_item->val_int());
break;
case TIME_TYPE:
grn_obj_reinit(ctx_, &value_, GRN_DB_TIME, 0);
{
MYSQL_TIME mysql_time;
get_time_value(field_item, const_item, &mysql_time);
bool truncated = false;
TimeConverter time_converter;
long long int time =
time_converter.mysql_time_to_grn_time(&mysql_time, &truncated);
GRN_TIME_SET(ctx_, &value_, time);
}
break;
case UNSUPPORTED_TYPE:
// Should not be occurred.
DBUG_PRINT("error",
("mroonga: append_const_item: unsupported type: <%d> "
"This case should not be occurred.",
field_type));
grn_obj_reinit(ctx_, &value_, GRN_DB_VOID, 0);
break;
}
grn_expr_append_const(ctx_, expression, &value_, GRN_OP_PUSH, 1);
DBUG_VOID_RETURN;
}
}