Merge remote-tracking branch 'origin/10.2' into bb-10.2-ext

This commit is contained in:
Alexander Barkov 2017-11-29 12:06:48 +04:00
commit 5b697c5a23
1904 changed files with 173141 additions and 109400 deletions

View file

@ -28,4 +28,23 @@ libmrn_need_mysql_la_SOURCES = \
mrn_value_decoder.cpp \
mrn_value_decoder.hpp \
mrn_database_repairer.cpp \
mrn_database_repairer.hpp
mrn_database_repairer.hpp \
mrn_context_pool.cpp \
mrn_context_pool.hpp \
mrn_operations.cpp \
mrn_operations.hpp \
mrn_operation.cpp \
mrn_operation.hpp \
mrn_database.cpp \
mrn_database.hpp \
mrn_column_name.cpp \
mrn_column_name.hpp \
mrn_count_skip_checker.cpp \
mrn_count_skip_checker.hpp \
mrn_query_parser.cpp \
mrn_query_parser.hpp \
mrn_current_thread.hpp \
mrn_smart_bitmap.cpp \
mrn_smart_bitmap.hpp \
mrn_table_fields_offset_mover.cpp \
mrn_table_fields_offset_mover.hpp

View file

@ -0,0 +1,69 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2016 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
*/
#include <mrn_mysql.h>
#include <mrn_mysql_compat.h>
#include "mrn_column_name.hpp"
#include <strfunc.h>
#include <string.h>
// for debug
#define MRN_CLASS_NAME "mrn::ColumnName"
namespace mrn {
ColumnName::ColumnName(const char *mysql_name)
: mysql_name_(mysql_name) {
encode(mysql_name, strlen(mysql_name));
}
ColumnName::ColumnName(const LEX_CSTRING &mysql_name)
: mysql_name_(mysql_name.str) {
encode(mysql_name.str, mysql_name.length);
}
const char *ColumnName::mysql_name() {
return mysql_name_;
}
const char *ColumnName::c_str() {
return name_;
}
size_t ColumnName::length() {
return length_;
}
void ColumnName::encode(const char *mysql_name,
size_t mysql_name_length) {
MRN_DBUG_ENTER_METHOD();
uint errors;
length_ = mrn_strconvert(system_charset_info,
mysql_name,
mysql_name_length,
&my_charset_filename,
name_,
MRN_MAX_PATH_SIZE,
&errors);
name_[length_] = '\0';
DBUG_VOID_RETURN;
}
}

View file

@ -0,0 +1,39 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2016 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
*/
#pragma once
#include <mrn_constants.hpp>
namespace mrn {
class ColumnName {
public:
ColumnName(const char *mysql_name);
ColumnName(const LEX_CSTRING &mysql_name);
const char *mysql_name();
const char *c_str();
size_t length();
private:
const char *mysql_name_;
char name_[MRN_MAX_PATH_SIZE];
size_t length_;
void encode(const char *mysql_name, size_t mysql_name_length);
};
}

View file

@ -1,6 +1,6 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2013-2014 Kouhei Sutou <kou@clear-code.com>
Copyright(C) 2013-2017 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
@ -175,7 +175,7 @@ namespace mrn {
bool convertable = false;
enum_field_types field_type = field_item->field_type();
enum_field_types field_type = field_item->field->real_type();
NormalizedType normalized_type = normalize_field_type(field_type);
switch (normalized_type) {
case STRING_TYPE:
@ -185,7 +185,12 @@ namespace mrn {
}
break;
case INT_TYPE:
convertable = value_item->type() == Item::INT_ITEM;
if (field_type == MYSQL_TYPE_ENUM) {
convertable = (value_item->type() == Item::STRING_ITEM ||
value_item->type() == Item::INT_ITEM);
} else {
convertable = value_item->type() == Item::INT_ITEM;
}
break;
case TIME_TYPE:
if (is_valid_time_value(field_item, value_item)) {
@ -206,7 +211,7 @@ namespace mrn {
bool convertable = false;
enum_field_types field_type = field_item->field_type();
enum_field_types field_type = field_item->field->type();
NormalizedType normalized_type = normalize_field_type(field_type);
switch (normalized_type) {
case STRING_TYPE:
@ -251,7 +256,7 @@ namespace mrn {
bool error;
Item *real_value_item = value_item->real_item();
switch (field_item->field_type()) {
switch (field_item->field->type()) {
case MYSQL_TYPE_TIME:
error = real_value_item->get_time(mysql_time);
break;
@ -352,6 +357,11 @@ namespace mrn {
case MYSQL_TYPE_GEOMETRY:
type = UNSUPPORTED_TYPE;
break;
#ifdef MRN_HAVE_MYSQL_TYPE_JSON
case MYSQL_TYPE_JSON:
type = STRING_TYPE;
break;
#endif
}
DBUG_RETURN(type);
@ -404,11 +414,11 @@ namespace mrn {
DBUG_RETURN(have);
}
const Item_func *ConditionConverter::find_match_against(const Item *item) {
unsigned int ConditionConverter::count_match_against(const Item *item) {
MRN_DBUG_ENTER_METHOD();
if (!item) {
DBUG_RETURN(NULL);
DBUG_RETURN(0);
}
switch (item->type()) {
@ -416,14 +426,13 @@ namespace mrn {
if (is_storage_mode_) {
Item_cond *cond_item = (Item_cond *)item;
if (cond_item->functype() == Item_func::COND_AND_FUNC) {
unsigned int n_match_againsts = 0;
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);
}
n_match_againsts += count_match_against(sub_item);
}
DBUG_RETURN(n_match_againsts);
}
}
break;
@ -432,7 +441,7 @@ namespace mrn {
const Item_func *func_item = (const Item_func *)item;
switch (func_item->functype()) {
case Item_func::FT_FUNC:
DBUG_RETURN(func_item);
DBUG_RETURN(1);
break;
default:
break;
@ -443,7 +452,7 @@ namespace mrn {
break;
}
DBUG_RETURN(NULL);
DBUG_RETURN(0);
}
void ConditionConverter::convert(const Item *where, grn_obj *expression) {
@ -560,7 +569,7 @@ namespace mrn {
grn_obj *expression) {
MRN_DBUG_ENTER_METHOD();
enum_field_types field_type = field_item->field_type();
enum_field_types field_type = field_item->field->real_type();
NormalizedType normalized_type = normalize_field_type(field_type);
switch (normalized_type) {
@ -574,7 +583,21 @@ namespace mrn {
break;
case INT_TYPE:
grn_obj_reinit(ctx_, &value_, GRN_DB_INT64, 0);
GRN_INT64_SET(ctx_, &value_, const_item->val_int());
if (field_type == MYSQL_TYPE_ENUM) {
if (const_item->type() == Item::STRING_ITEM) {
String *string;
string = const_item->val_str(NULL);
Field_enum *enum_field = static_cast<Field_enum *>(field_item->field);
int enum_value = find_type(string->c_ptr(),
enum_field->typelib,
FIND_TYPE_BASIC);
GRN_INT64_SET(ctx_, &value_, enum_value);
} else {
GRN_INT64_SET(ctx_, &value_, const_item->val_int());
}
} else {
GRN_INT64_SET(ctx_, &value_, const_item->val_int());
}
break;
case TIME_TYPE:
grn_obj_reinit(ctx_, &value_, GRN_DB_TIME, 0);

View file

@ -33,7 +33,7 @@ namespace mrn {
~ConditionConverter();
bool is_convertable(const Item *item);
const Item_func *find_match_against(const Item *item);
unsigned int count_match_against(const Item *item);
// caller must check "where" can be convertable by
// is_convertable(). This method doesn't validate "where".
void convert(const Item *where, grn_obj *expression);

View file

@ -0,0 +1,120 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2015 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
*/
#include "mrn_context_pool.hpp"
#include "mrn_lock.hpp"
#include <time.h>
namespace mrn {
// for debug
#define MRN_CLASS_NAME "mrn::ContextPool::Impl"
class ContextPool::Impl {
public:
Impl(mysql_mutex_t *mutex)
: mutex_(mutex),
pool_(NULL),
last_pull_time_(0) {
}
~Impl(void) {
clear();
}
grn_ctx *pull(void) {
MRN_DBUG_ENTER_METHOD();
grn_ctx *ctx = NULL;
{
time_t now;
time(&now);
mrn::Lock lock(mutex_);
if (pool_) {
ctx = static_cast<grn_ctx *>(pool_->data);
list_pop(pool_);
if ((now - last_pull_time_) >= CLEAR_THREATHOLD_IN_SECONDS) {
clear();
}
}
last_pull_time_ = now;
}
if (!ctx) {
ctx = grn_ctx_open(0);
}
DBUG_RETURN(ctx);
}
void release(grn_ctx *ctx) {
MRN_DBUG_ENTER_METHOD();
{
mrn::Lock lock(mutex_);
list_push(pool_, ctx);
grn_ctx_use(ctx, NULL);
}
DBUG_VOID_RETURN;
}
private:
static const unsigned int CLEAR_THREATHOLD_IN_SECONDS = 60 * 5;
mysql_mutex_t *mutex_;
LIST *pool_;
time_t last_pull_time_;
void clear(void) {
MRN_DBUG_ENTER_METHOD();
while (pool_) {
grn_ctx *ctx = static_cast<grn_ctx *>(pool_->data);
grn_ctx_close(ctx);
list_pop(pool_);
}
DBUG_VOID_RETURN;
}
};
// For debug
#undef MRN_CLASS_NAME
#define MRN_CLASS_NAME "mrn::ContextPool"
ContextPool::ContextPool(mysql_mutex_t *mutex)
: impl_(new Impl(mutex)) {
}
ContextPool::~ContextPool(void) {
delete impl_;
}
grn_ctx *ContextPool::pull(void) {
MRN_DBUG_ENTER_METHOD();
grn_ctx *ctx = impl_->pull();
DBUG_RETURN(ctx);
}
void ContextPool::release(grn_ctx *ctx) {
MRN_DBUG_ENTER_METHOD();
impl_->release(ctx);
DBUG_VOID_RETURN;
}
}

View file

@ -0,0 +1,41 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2015 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
*/
#ifndef MRN_CONTEXT_POOL_HPP_
#define MRN_CONTEXT_POOL_HPP_
#include <mrn_mysql.h>
#include <groonga.h>
namespace mrn {
class ContextPool {
public:
ContextPool(mysql_mutex_t *mutex);
~ContextPool(void);
grn_ctx *pull(void);
void release(grn_ctx *context);
private:
class Impl;
Impl *impl_;
};
}
#endif /* MRN_CONTEXT_POOL_HPP_ */

View file

@ -0,0 +1,303 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2010-2013 Kentoku SHIBA
Copyright(C) 2011-2017 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
*/
#include "mrn_count_skip_checker.hpp"
#include <item_sum.h>
// for debug
#define MRN_CLASS_NAME "mrn::CountSkipChecker"
namespace mrn {
CountSkipChecker::CountSkipChecker(grn_ctx *ctx,
TABLE *table,
SELECT_LEX *select_lex,
KEY *key_info,
key_part_map target_key_part_map,
bool is_storage_mode)
: ctx_(ctx),
table_(table),
select_lex_(select_lex),
key_info_(key_info),
target_key_part_map_(target_key_part_map),
is_storage_mode_(is_storage_mode) {
}
CountSkipChecker::~CountSkipChecker() {
}
bool CountSkipChecker::check() {
MRN_DBUG_ENTER_METHOD();
if (select_lex_->item_list.elements != 1) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] not only one item: %u",
select_lex_->item_list.elements);
DBUG_RETURN(false);
}
if (select_lex_->group_list.elements > 0) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] have groups: %u",
select_lex_->group_list.elements);
DBUG_RETURN(false);
}
if (MRN_SELECT_LEX_GET_HAVING_COND(select_lex_)) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] have HAVING");
DBUG_RETURN(false);
}
if (select_lex_->table_list.elements != 1) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] not only one table: %u",
select_lex_->table_list.elements);
DBUG_RETURN(false);
}
Item *info = static_cast<Item *>(select_lex_->item_list.first_node()->info);
if (info->type() != Item::SUM_FUNC_ITEM) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] item isn't sum function: %u",
info->type());
DBUG_RETURN(false);
}
Item_sum *sum_item = static_cast<Item_sum *>(info);
if (sum_item->sum_func() != Item_sum::COUNT_FUNC) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] not COUNT: %u",
sum_item->sum_func());
DBUG_RETURN(false);
}
if (ITEM_SUM_GET_NEST_LEVEL(sum_item) != 0 ||
ITEM_SUM_GET_AGGR_LEVEL(sum_item) != 0 ||
ITEM_SUM_GET_MAX_AGGR_LEVEL(sum_item) != -1 ||
sum_item->max_sum_func_level != -1) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] not simple COUNT(*): %d:%d:%d:%d",
ITEM_SUM_GET_NEST_LEVEL(sum_item),
ITEM_SUM_GET_AGGR_LEVEL(sum_item),
ITEM_SUM_GET_MAX_AGGR_LEVEL(sum_item),
sum_item->max_sum_func_level);
DBUG_RETURN(false);
}
Item *where = MRN_SELECT_LEX_GET_WHERE_COND(select_lex_);
if (!where) {
if (is_storage_mode_) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][true] no condition");
DBUG_RETURN(true);
} else {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] no condition with wrapper mode");
DBUG_RETURN(false);
}
}
bool skippable = is_skippable(where);
DBUG_RETURN(skippable);
}
bool CountSkipChecker::is_skippable(Item *where) {
MRN_DBUG_ENTER_METHOD();
bool skippable = false;
switch (where->type()) {
case Item::COND_ITEM:
{
Item_cond *cond_item = static_cast<Item_cond *>(where);
skippable = is_skippable(cond_item);
if (skippable) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][true] skippable multiple conditions");
}
}
break;
case Item::FUNC_ITEM:
{
Item_func *func_item = static_cast<Item_func *>(where);
if (func_item->functype() == Item_func::FT_FUNC) {
if (select_lex_->select_n_where_fields == 1) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][true] "
"only one full text search condition");
DBUG_RETURN(true);
} else {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] "
"full text search condition and more conditions: %u",
select_lex_->select_n_where_fields);
DBUG_RETURN(false);
}
} else {
skippable = is_skippable(func_item);
if (skippable) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][true] skippable condition");
}
}
}
break;
default:
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] unsupported top level item: %u",
where->type());
break;
}
DBUG_RETURN(skippable);
}
bool CountSkipChecker::is_skippable(Item_cond *cond_item) {
MRN_DBUG_ENTER_METHOD();
List_iterator<Item> iterator(*(cond_item->argument_list()));
Item *sub_item;
while ((sub_item = iterator++)) {
if (sub_item->type() != Item::FUNC_ITEM) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] "
"sub condition isn't function item: %u",
sub_item->type());
DBUG_RETURN(false);
}
if (!is_skippable(static_cast<Item_func *>(sub_item))) {
DBUG_RETURN(false);
}
}
DBUG_RETURN(true);
}
bool CountSkipChecker::is_skippable(Item_func *func_item) {
MRN_DBUG_ENTER_METHOD();
switch (func_item->functype()) {
case Item_func::EQ_FUNC:
case Item_func::EQUAL_FUNC:
case Item_func::NE_FUNC:
case Item_func::LT_FUNC:
case Item_func::LE_FUNC:
case Item_func::GE_FUNC:
case Item_func::GT_FUNC:
{
Item **arguments = func_item->arguments();
Item *left_item = arguments[0];
if (left_item->type() != Item::FIELD_ITEM) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] not field: %u:%u",
func_item->functype(),
left_item->type());
DBUG_RETURN(false);
}
bool skippable = is_skippable(static_cast<Item_field *>(left_item));
DBUG_RETURN(skippable);
}
break;
case Item_func::BETWEEN:
{
Item **arguments = func_item->arguments();
Item *target_item = arguments[0];
if (target_item->type() != Item::FIELD_ITEM) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] BETWEEN target isn't field: %u",
target_item->type());
DBUG_RETURN(false);
}
bool skippable = is_skippable(static_cast<Item_field *>(target_item));
DBUG_RETURN(skippable);
}
break;
case Item_func::MULT_EQUAL_FUNC:
#ifdef MRN_HAVE_ITEM_EQUAL_FIELDS_ITERATOR
{
Item_equal *equal_item = static_cast<Item_equal *>(func_item);
Item_equal_fields_iterator iterator(*equal_item);
Item *field_item;
while ((field_item = iterator++)) {
bool skippable = is_skippable(static_cast<Item_field *>(field_item));
if (!skippable) {
DBUG_RETURN(skippable);
}
}
DBUG_RETURN(true);
}
#endif
break;
default:
break;
}
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] unsupported function item: %u",
func_item->functype());
DBUG_RETURN(false);
}
bool CountSkipChecker::is_skippable(Item_field *field_item) {
MRN_DBUG_ENTER_METHOD();
Field *field = field_item->field;
if (!field) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] field is missing");
DBUG_RETURN(false);
}
if (field->table != table_) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] external table's field");
DBUG_RETURN(false);
}
if (!key_info_) {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] no active index: <%s>:<%s>",
*(field->table_name),
field->field_name.str);
DBUG_RETURN(false);
}
uint i;
KEY_PART_INFO *key_part = key_info_->key_part;
for (i = 0; i < KEY_N_KEY_PARTS(key_info_); i++) {
if (key_part[i].field == field) {
if ((target_key_part_map_ >> i) & 1) {
DBUG_RETURN(true);
} else {
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] "
"field's index are out of key part map: %u:%lu: <%s>:<%s>",
i,
target_key_part_map_,
*(field->table_name),
field->field_name.str);
DBUG_RETURN(false);
}
}
}
GRN_LOG(ctx_, GRN_LOG_DEBUG,
"[mroonga][count-skip][false] field isn't indexed: <%s>:<%s>",
*(field->table_name),
field->field_name.str);
DBUG_RETURN(false);
}
}

View file

@ -0,0 +1,57 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2016 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
*/
#ifndef MRN_COUNT_SKIP_CHECKER_HPP_
#define MRN_COUNT_SKIP_CHECKER_HPP_
#include <mrn_mysql_compat.h>
#include <item_cmpfunc.h>
#include <groonga.h>
namespace mrn {
class CountSkipChecker {
public:
CountSkipChecker(grn_ctx *ctx,
TABLE *table,
SELECT_LEX *select_lex,
KEY *key_info,
key_part_map target_key_part_map,
bool is_storage_mode);
~CountSkipChecker();
bool check();
private:
grn_ctx *ctx_;
TABLE *table_;
SELECT_LEX *select_lex_;
KEY *key_info_;
key_part_map target_key_part_map_;
bool is_storage_mode_;
bool is_skippable(Item *where);
bool is_skippable(Item_cond *cond_item);
bool is_skippable(Item_func *func_item);
bool is_skippable(Item_field *field_item);
};
}
#endif /* MRN_COUNT_SKIP_CHECKER_HPP_ */

View file

@ -0,0 +1,27 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2017 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
*/
#pragma once
#include <mrn_mysql.h>
#include <mrn_mysql_compat.h>
#if (!defined(MRN_MARIADB_P) && MYSQL_VERSION_ID >= 80002)
# include <current_thd.h>
#endif

View file

@ -0,0 +1,89 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2015 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
*/
#include <mrn_mysql.h>
#include "mrn_database.hpp"
#include "mrn_operations.hpp"
// for debug
#define MRN_CLASS_NAME "mrn::Database"
namespace mrn {
Database::Database(grn_ctx *ctx, grn_obj *db)
: ctx_(ctx),
db_(db),
broken_table_names_(NULL),
is_broken_(false) {
Operations operations(ctx_);
broken_table_names_ = operations.collect_processing_table_names();
is_broken_ = operations.is_locked();
}
Database::~Database(void) {
close();
}
void Database::close() {
MRN_DBUG_ENTER_METHOD();
if (db_) {
grn_hash_close(ctx_, broken_table_names_);
broken_table_names_ = NULL;
grn_obj_close(ctx_, db_);
db_ = NULL;
}
DBUG_VOID_RETURN;
}
grn_rc Database::remove() {
MRN_DBUG_ENTER_METHOD();
grn_rc rc = GRN_SUCCESS;
if (db_) {
grn_hash_close(ctx_, broken_table_names_);
broken_table_names_ = NULL;
rc = grn_obj_remove(ctx_, db_);
if (rc == GRN_SUCCESS) {
db_ = NULL;
}
}
DBUG_RETURN(rc);
}
grn_obj *Database::get() {
MRN_DBUG_ENTER_METHOD();
DBUG_RETURN(db_);
}
bool Database::is_broken() {
MRN_DBUG_ENTER_METHOD();
DBUG_RETURN(is_broken_);
}
bool Database::is_broken_table(const char *name, size_t name_size) {
MRN_DBUG_ENTER_METHOD();
grn_id id = grn_hash_get(ctx_, broken_table_names_, name, name_size, NULL);
DBUG_RETURN(id != GRN_ID_NIL);
}
void Database::mark_table_repaired(const char *name, size_t name_size) {
MRN_DBUG_ENTER_METHOD();
grn_hash_delete(ctx_, broken_table_names_, name, name_size, NULL);
DBUG_VOID_RETURN;
}
}

View file

@ -0,0 +1,47 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2015 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
*/
#ifndef MRN_DATABASE_HPP_
#define MRN_DATABASE_HPP_
#include <groonga.h>
namespace mrn {
class Database {
public:
Database(grn_ctx *ctx, grn_obj *db);
~Database(void);
void close();
grn_rc remove();
grn_obj *get();
bool is_broken();
bool is_broken_table(const char *name, size_t name_size);
void mark_table_repaired(const char *name, size_t name_size);
private:
grn_ctx *ctx_;
grn_obj *db_;
grn_hash *broken_table_names_;
bool is_broken_;
};
}
#endif /* MRN_DATABASE_HPP_ */

View file

@ -56,9 +56,9 @@ namespace mrn {
if (cache_) {
void *db_address;
GRN_HASH_EACH(ctx_, cache_, id, NULL, 0, &db_address, {
grn_obj *db;
Database *db;
memcpy(&db, db_address, sizeof(grn_obj *));
grn_obj_unlink(ctx_, db);
delete db;
});
grn_hash_close(ctx_, cache_);
}
@ -80,7 +80,7 @@ namespace mrn {
DBUG_RETURN(true);
}
int DatabaseManager::open(const char *path, grn_obj **db) {
int DatabaseManager::open(const char *path, Database **db) {
MRN_DBUG_ENTER_METHOD();
int error = 0;
@ -100,36 +100,54 @@ namespace mrn {
mapper.db_name(), strlen(mapper.db_name()),
&db_address);
if (id == GRN_ID_NIL) {
grn_obj *grn_db;
struct stat db_stat;
if (stat(mapper.db_path(), &db_stat)) {
GRN_LOG(ctx_, GRN_LOG_INFO,
"database not found. creating...: <%s>", mapper.db_path());
if (path[0] == FN_CURLIB &&
(path[1] == FN_LIBCHAR || path[1] == FN_LIBCHAR2)) {
mrn_is_directory_separator(path[1])) {
ensure_database_directory();
}
*db = grn_db_create(ctx_, mapper.db_path(), NULL);
grn_db = grn_db_create(ctx_, mapper.db_path(), NULL);
if (ctx_->rc) {
error = ER_CANT_CREATE_TABLE;
my_message(error, ctx_->errbuf, MYF(0));
DBUG_RETURN(error);
}
} else {
*db = grn_db_open(ctx_, mapper.db_path());
grn_db = grn_db_open(ctx_, mapper.db_path());
if (ctx_->rc) {
error = ER_CANT_OPEN_FILE;
my_message(error, ctx_->errbuf, MYF(0));
DBUG_RETURN(error);
}
}
*db = new Database(ctx_, grn_db);
grn_hash_add(ctx_, cache_,
mapper.db_name(), strlen(mapper.db_name()),
&db_address, NULL);
memcpy(db_address, db, sizeof(grn_obj *));
error = ensure_normalizers_registered(*db);
memcpy(db_address, db, sizeof(Database *));
error = ensure_normalizers_registered((*db)->get());
if (!error) {
if ((*db)->is_broken()) {
error = ER_CANT_OPEN_FILE;
char error_message[MRN_MESSAGE_BUFFER_SIZE];
snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
"mroonga: database: open: "
"The database maybe broken. "
"We recommend you to recreate the database. "
"If the database isn't broken, "
"you can remove this error by running "
"'groonga %s table_remove mroonga_operations' "
"on server. But the latter isn't recommended.",
mapper.db_path());
my_message(error, error_message, MYF(0));
}
}
} else {
memcpy(db, db_address, sizeof(grn_obj *));
grn_ctx_use(ctx_, *db);
memcpy(db, db_address, sizeof(Database *));
grn_ctx_use(ctx_, (*db)->get());
}
DBUG_RETURN(error);
@ -150,10 +168,11 @@ namespace mrn {
DBUG_VOID_RETURN;
}
grn_obj *db = NULL;
memcpy(&db, db_address, sizeof(grn_obj *));
Database *db = NULL;
memcpy(&db, db_address, sizeof(Database *));
grn_ctx_use(ctx_, db->get());
if (db) {
grn_obj_close(ctx_, db);
delete db;
}
grn_hash_delete_by_id(ctx_, cache_, id, NULL);
@ -173,29 +192,35 @@ namespace mrn {
mapper.db_name(), strlen(mapper.db_name()),
&db_address);
grn_obj *db = NULL;
Database *db = NULL;
if (id == GRN_ID_NIL) {
struct stat dummy;
if (stat(mapper.db_path(), &dummy) == 0) {
db = grn_db_open(ctx_, mapper.db_path());
grn_obj *grn_db = grn_db_open(ctx_, mapper.db_path());
db = new Database(ctx_, grn_db);
}
} else {
memcpy(&db, db_address, sizeof(grn_obj *));
memcpy(&db, db_address, sizeof(Database *));
grn_ctx_use(ctx_, db->get());
}
if (!db) {
DBUG_RETURN(false);
}
if (grn_obj_remove(ctx_, db) == GRN_SUCCESS) {
if (db->remove() == GRN_SUCCESS) {
if (id != GRN_ID_NIL) {
grn_hash_delete_by_id(ctx_, cache_, id, NULL);
}
delete db;
DBUG_RETURN(true);
} else {
GRN_LOG(ctx_, GRN_LOG_ERROR,
"failed to drop database: <%s>: <%s>",
mapper.db_path(), ctx_->errbuf);
if (id == GRN_ID_NIL) {
delete db;
}
DBUG_RETURN(false);
}
}
@ -223,22 +248,28 @@ namespace mrn {
break;
}
void *db_address;
grn_obj *db;
Database *db;
grn_hash_cursor_get_value(ctx_, cursor, &db_address);
memcpy(&db, db_address, sizeof(grn_obj *));
memcpy(&db, db_address, sizeof(Database *));
grn_ctx_use(ctx_, db->get());
grn_rc rc = grn_hash_cursor_delete(ctx_, cursor, NULL);
if (rc) {
error = ER_ERROR_ON_READ;
my_message(error, ctx_->errbuf, MYF(0));
break;
}
grn_obj_close(ctx_, db);
delete db;
}
grn_hash_cursor_close(ctx_, cursor);
DBUG_RETURN(error);
}
const char *DatabaseManager::error_message() {
MRN_DBUG_ENTER_METHOD();
DBUG_RETURN(ctx_->errbuf);
}
void DatabaseManager::mkdir_p(const char *directory) {
MRN_DBUG_ENTER_METHOD();
@ -246,8 +277,7 @@ namespace mrn {
char sub_directory[MRN_MAX_PATH_SIZE];
sub_directory[0] = '\0';
while (true) {
if (directory[i] == FN_LIBCHAR ||
directory[i] == FN_LIBCHAR2 ||
if (mrn_is_directory_separator(directory[i]) ||
directory[i] == '\0') {
sub_directory[i] = '\0';
struct stat directory_status;
@ -290,8 +320,10 @@ namespace mrn {
const char *last_path_separator;
last_path_separator = strrchr(path_prefix, FN_LIBCHAR);
#ifdef FN_LIBCHAR2
if (!last_path_separator)
last_path_separator = strrchr(path_prefix, FN_LIBCHAR2);
#endif
if (!last_path_separator)
DBUG_VOID_RETURN;
if (path_prefix == last_path_separator)

View file

@ -22,6 +22,8 @@
#ifndef MRN_DATABASE_MANAGER_HPP_
#define MRN_DATABASE_MANAGER_HPP_
#include "mrn_database.hpp"
#include <groonga.h>
namespace mrn {
@ -30,10 +32,11 @@ namespace mrn {
DatabaseManager(grn_ctx *ctx, mysql_mutex_t *mutex);
~DatabaseManager(void);
bool init(void);
int open(const char *path, grn_obj **db);
int open(const char *path, Database **db);
void close(const char *path);
bool drop(const char *path);
int clear(void);
const char *error_message();
private:
grn_ctx *ctx_;

View file

@ -1,6 +1,6 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2015 Kouhei Sutou <kou@clear-code.com>
Copyright(C) 2015-2017 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
@ -36,6 +36,16 @@
#endif
namespace mrn {
struct CheckResult {
CheckResult() :
is_crashed(false),
is_corrupt(false) {
}
bool is_crashed;
bool is_corrupt;
};
DatabaseRepairer::DatabaseRepairer(grn_ctx *ctx, THD *thd)
: ctx_(ctx),
thd_(thd),
@ -53,10 +63,19 @@ namespace mrn {
bool DatabaseRepairer::is_crashed(void) {
MRN_DBUG_ENTER_METHOD();
bool is_crashed = false;
each_database(&DatabaseRepairer::is_crashed_body, &is_crashed);
CheckResult result;
each_database(&DatabaseRepairer::check_body, &result);
DBUG_RETURN(is_crashed);
DBUG_RETURN(result.is_crashed);
}
bool DatabaseRepairer::is_corrupt(void) {
MRN_DBUG_ENTER_METHOD();
CheckResult result;
each_database(&DatabaseRepairer::check_body, &result);
DBUG_RETURN(result.is_corrupt);
}
bool DatabaseRepairer::repair(void) {
@ -81,9 +100,19 @@ namespace mrn {
DBUG_VOID_RETURN;
}
do {
each_database_body(data.cFileName, each_body_func, user_data);
} while (FindNextFile(finder, &data) != 0);
grn_ctx ctx;
grn_rc rc = grn_ctx_init(&ctx, 0);
if (rc == GRN_SUCCESS) {
do {
each_database_body(data.cFileName, &ctx, each_body_func, user_data);
} while (FindNextFile(finder, &data) != 0);
grn_ctx_fin(&ctx);
} else {
GRN_LOG(ctx_, GRN_LOG_WARNING,
"[mroonga][database][repairer][each] "
"failed to initialize grn_ctx: %d: %s",
rc, grn_rc_to_string(rc));
}
FindClose(finder);
#else
DIR *dir = opendir(base_directory_);
@ -91,8 +120,18 @@ namespace mrn {
DBUG_VOID_RETURN;
}
while (struct dirent *entry = readdir(dir)) {
each_database_body(entry->d_name, each_body_func, user_data);
grn_ctx ctx;
grn_rc rc = grn_ctx_init(&ctx, 0);
if (rc == GRN_SUCCESS) {
while (struct dirent *entry = readdir(dir)) {
each_database_body(entry->d_name, &ctx, each_body_func, user_data);
}
grn_ctx_fin(&ctx);
} else {
GRN_LOG(ctx_, GRN_LOG_WARNING,
"[mroonga][database][repairer][each] "
"failed to initialize grn_ctx: %d: %s",
rc, grn_rc_to_string(rc));
}
closedir(dir);
#endif
@ -101,6 +140,7 @@ namespace mrn {
}
void DatabaseRepairer::each_database_body(const char *base_path,
grn_ctx *ctx,
EachBodyFunc each_body_func,
void *user_data) {
MRN_DBUG_ENTER_METHOD();
@ -123,14 +163,14 @@ namespace mrn {
char db_path[MRN_MAX_PATH_SIZE];
snprintf(db_path, MRN_MAX_PATH_SIZE,
"%s%c%s", base_directory_, FN_LIBCHAR, base_path);
grn_obj *db = grn_db_open(ctx_, db_path);
grn_obj *db = grn_db_open(ctx, db_path);
if (!db) {
DBUG_VOID_RETURN;
}
(this->*each_body_func)(db, db_path, user_data);
(this->*each_body_func)(ctx, db, db_path, user_data);
grn_obj_close(ctx_, db);
grn_obj_close(ctx, db);
DBUG_VOID_RETURN;
}
@ -150,8 +190,7 @@ namespace mrn {
size_t raw_path_prefix_length = strlen(raw_path_prefix);
size_t separator_position = raw_path_prefix_length;
for (; separator_position > 0; separator_position--) {
if (base_directory_buffer_[separator_position] == FN_LIBCHAR ||
base_directory_buffer_[separator_position] == FN_LIBCHAR2) {
if (mrn_is_directory_separator(base_directory_buffer_[separator_position])) {
break;
}
}
@ -169,34 +208,46 @@ namespace mrn {
DBUG_VOID_RETURN;
}
void DatabaseRepairer::is_crashed_body(grn_obj *db,
const char *db_path,
void *user_data) {
void DatabaseRepairer::check_body(grn_ctx *ctx,
grn_obj *db,
const char *db_path,
void *user_data) {
MRN_DBUG_ENTER_METHOD();
bool *is_crashed = static_cast<bool *>(user_data);
CheckResult *result = static_cast<CheckResult *>(user_data);
if (grn_obj_is_locked(ctx_, db)) {
*is_crashed = true;
if (grn_obj_is_locked(ctx, db)) {
result->is_crashed = true;
result->is_corrupt = true;
DBUG_VOID_RETURN;
}
grn_table_cursor *cursor;
cursor = grn_table_cursor_open(ctx_, db,
cursor = grn_table_cursor_open(ctx, db,
NULL, 0,
NULL, 0,
0, -1, GRN_CURSOR_BY_ID);
if (!cursor) {
*is_crashed = true;
result->is_crashed = true;
result->is_corrupt = true;
DBUG_VOID_RETURN;
}
grn_id id;
while ((id = grn_table_cursor_next(ctx_, cursor)) != GRN_ID_NIL) {
grn_obj *object = grn_ctx_at(ctx_, id);
while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) {
if (grn_id_is_builtin(ctx, id)) {
continue;
}
grn_obj *object = grn_ctx_at(ctx, id);
if (!object) {
continue;
if (ctx->rc == GRN_SUCCESS) {
continue;
} else {
result->is_corrupt = true;
break;
}
}
switch (object->header.type) {
@ -207,37 +258,40 @@ namespace mrn {
case GRN_COLUMN_FIX_SIZE:
case GRN_COLUMN_VAR_SIZE:
case GRN_COLUMN_INDEX:
grn_obj_is_locked(ctx_, object);
*is_crashed = true;
if (grn_obj_is_locked(ctx_, object)) {
result->is_crashed = true;
result->is_corrupt = true;
}
break;
default:
break;
}
grn_obj_unlink(ctx_, object);
grn_obj_unlink(ctx, object);
if (*is_crashed) {
if (result->is_crashed || result->is_corrupt) {
break;
}
}
grn_table_cursor_close(ctx_, cursor);
grn_table_cursor_close(ctx, cursor);
DBUG_VOID_RETURN;
}
void DatabaseRepairer::repair_body(grn_obj *db,
void DatabaseRepairer::repair_body(grn_ctx *ctx,
grn_obj *db,
const char *db_path,
void *user_data) {
MRN_DBUG_ENTER_METHOD();
bool *succeeded = static_cast<bool *>(user_data);
if (grn_db_recover(ctx_, db) != GRN_SUCCESS) {
if (grn_db_recover(ctx, db) != GRN_SUCCESS) {
push_warning_printf(thd_,
MRN_SEVERITY_WARNING,
ER_NOT_KEYFILE,
"mroonga: repair: "
"Failed to recover database: <%s>: <%s>",
db_path, ctx_->errbuf);
db_path, ctx->errbuf);
*succeeded = false;
}

View file

@ -1,6 +1,6 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2015 Kouhei Sutou <kou@clear-code.com>
Copyright(C) 2015-2017 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
@ -28,6 +28,7 @@ namespace mrn {
DatabaseRepairer(grn_ctx *ctx, THD *thd);
~DatabaseRepairer(void);
bool is_crashed(void);
bool is_corrupt(void);
bool repair(void);
private:
@ -40,18 +41,26 @@ namespace mrn {
size_t path_prefix_length_;
size_t mrn_db_file_suffix_length_;
typedef void (DatabaseRepairer::*EachBodyFunc)(grn_obj *db,
typedef void (DatabaseRepairer::*EachBodyFunc)(grn_ctx *ctx,
grn_obj *db,
const char *db_path,
void *user_data);
void each_database(EachBodyFunc each_body_func, void *user_data);
void each_database_body(const char *base_path,
grn_ctx *ctx,
EachBodyFunc each_body_func,
void *user_data);
void detect_paths(void);
void is_crashed_body(grn_obj *db, const char *db_path, void *user_data);
void repair_body(grn_obj *db, const char *db_path, void *user_data);
void check_body(grn_ctx *ctx,
grn_obj *db,
const char *db_path,
void *user_data);
void repair_body(grn_ctx *ctx,
grn_obj *db,
const char *db_path,
void *user_data);
};
}

View file

@ -1,7 +1,7 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2011 Kentoku SHIBA
Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
Copyright(C) 2011-2015 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
@ -26,7 +26,8 @@
#define MRN_CLASS_NAME "mrn::IndexTableName"
namespace mrn {
const char *IndexTableName::SEPARATOR = "-";
const char *IndexTableName::SEPARATOR = "#";
const char *IndexTableName::OLD_SEPARATOR = "-";
bool IndexTableName::is_custom_name(const char *table_name,
size_t table_name_length,
@ -43,9 +44,12 @@ namespace mrn {
DBUG_RETURN(true);
}
if (strncmp(SEPARATOR,
index_table_name + table_name_length,
strlen(SEPARATOR)) != 0) {
if ((strncmp(OLD_SEPARATOR,
index_table_name + table_name_length,
strlen(OLD_SEPARATOR)) != 0) &&
(strncmp(SEPARATOR,
index_table_name + table_name_length,
strlen(SEPARATOR)) != 0)) {
DBUG_RETURN(true);
}
@ -63,6 +67,12 @@ namespace mrn {
encoded_mysql_index_name_multibyte + MRN_MAX_KEY_SIZE,
mysql_index_name_multibyte,
mysql_index_name_multibyte + strlen(mysql_index_name_));
snprintf(old_name_, MRN_MAX_KEY_SIZE,
"%s%s%s",
table_name_,
OLD_SEPARATOR,
encoded_mysql_index_name_multibyte);
old_length_ = strlen(old_name_);
snprintf(name_, MRN_MAX_KEY_SIZE,
"%s%s%s",
table_name_,
@ -79,6 +89,14 @@ namespace mrn {
return length_;
}
const char *IndexTableName::old_c_str() {
return old_name_;
}
size_t IndexTableName::old_length() {
return old_length_;
}
uint IndexTableName::encode(uchar *encoded_start,
uchar *encoded_end,
const uchar *mysql_string_start,

View file

@ -1,7 +1,7 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2011 Kentoku SHIBA
Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
Copyright(C) 2011-2015 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
@ -27,6 +27,7 @@ namespace mrn {
class IndexTableName {
public:
static const char *SEPARATOR;
static const char *OLD_SEPARATOR;
static bool is_custom_name(const char *table_name,
size_t table_name_length,
@ -36,9 +37,13 @@ namespace mrn {
IndexTableName(const char *table_name, const char *mysql_index_name);
const char *c_str();
size_t length();
const char *old_c_str();
size_t old_length();
private:
const char *table_name_;
const char *mysql_index_name_;
char old_name_[MRN_MAX_KEY_SIZE];
size_t old_length_;
char name_[MRN_MAX_KEY_SIZE];
size_t length_;

View file

@ -288,6 +288,7 @@ namespace mrn {
decode_long_long_int(current_grn_key, &grn_time);
TimeConverter time_converter;
MYSQL_TIME mysql_time;
mysql_time.neg = FALSE;
mysql_time.time_type = MYSQL_TIMESTAMP_DATETIME;
time_converter.grn_time_to_mysql_time(grn_time, &mysql_time);
long long int mysql_datetime_packed =
@ -518,6 +519,14 @@ namespace mrn {
*data_type = TYPE_BYTE_SEQUENCE;
*data_size = key_part->length;
break;
#ifdef MRN_HAVE_MYSQL_TYPE_JSON
case MYSQL_TYPE_JSON:
// TODO
DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_JSON"));
*data_type = TYPE_BYTE_SEQUENCE;
*data_size = key_part->length;
break;
#endif
}
DBUG_VOID_RETURN;
}

View file

@ -20,11 +20,11 @@
#ifndef MRN_MULTIPLE_COLUMN_KEY_CODEC_HPP_
#define MRN_MULTIPLE_COLUMN_KEY_CODEC_HPP_
#include <groonga.h>
#include <mrn_mysql.h>
#include <mrn_mysql_compat.h>
#include <groonga.h>
namespace mrn {
class MultipleColumnKeyCodec {
public:

View file

@ -0,0 +1,51 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2015 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
*/
#include <mrn_mysql.h>
#include "mrn_operation.hpp"
// for debug
#define MRN_CLASS_NAME "mrn::Operation"
namespace mrn {
Operation::Operation(mrn::Operations *operations,
const char *type,
const char *table_name,
size_t table_name_size)
: operations_(operations),
id_(operations_->start(type, table_name, table_name_size)) {
}
Operation::~Operation() {
MRN_DBUG_ENTER_METHOD();
operations_->finish(id_);
DBUG_VOID_RETURN;
}
void Operation::record_target(grn_id record_id) {
MRN_DBUG_ENTER_METHOD();
operations_->record_target(id_, record_id);
DBUG_VOID_RETURN;
}
}

View file

@ -0,0 +1,42 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2015 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
*/
#ifndef MRN_OPERATION_HPP_
#define MRN_OPERATION_HPP_
#include <mrn_operations.hpp>
namespace mrn {
class Operation {
public:
Operation(mrn::Operations *operations,
const char *type,
const char *table_name,
size_t table_name_size);
~Operation();
void record_target(grn_id record_id);
private:
mrn::Operations *operations_;
grn_id id_;
};
}
#endif /* MRN_OPERATION_HPP_ */

View file

@ -0,0 +1,401 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2015 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
*/
#include <mrn_mysql.h>
#include <string.h>
#include "mrn_operations.hpp"
// for debug
#define MRN_CLASS_NAME "mrn::Operations"
#define TABLE_NAME "mroonga_operations"
#define COLUMN_TYPE_NAME "type"
#define COLUMN_TABLE_NAME "table"
#define COLUMN_RECORD_NAME "record"
namespace mrn {
Operations::Operations(grn_ctx *ctx)
: ctx_(ctx) {
MRN_DBUG_ENTER_METHOD();
GRN_TEXT_INIT(&text_buffer_, GRN_OBJ_DO_SHALLOW_COPY);
GRN_UINT32_INIT(&id_buffer_, 0);
table_ = grn_ctx_get(ctx_, TABLE_NAME, -1);
if (!table_) {
table_ = grn_table_create(ctx_,
TABLE_NAME, strlen(TABLE_NAME),
NULL,
GRN_OBJ_TABLE_NO_KEY | GRN_OBJ_PERSISTENT,
NULL, NULL);
columns_.type_ =
grn_column_create(ctx_, table_,
COLUMN_TYPE_NAME, strlen(COLUMN_TYPE_NAME),
NULL,
GRN_OBJ_COLUMN_SCALAR | GRN_OBJ_PERSISTENT,
grn_ctx_at(ctx_, GRN_DB_SHORT_TEXT));
columns_.table_ =
grn_column_create(ctx_, table_,
COLUMN_TABLE_NAME, strlen(COLUMN_TABLE_NAME),
NULL,
GRN_OBJ_COLUMN_SCALAR | GRN_OBJ_PERSISTENT,
grn_ctx_at(ctx_, GRN_DB_SHORT_TEXT));
columns_.record_ =
grn_column_create(ctx_, table_,
COLUMN_RECORD_NAME, strlen(COLUMN_RECORD_NAME),
NULL,
GRN_OBJ_COLUMN_SCALAR | GRN_OBJ_PERSISTENT,
grn_ctx_at(ctx_, GRN_DB_UINT32));
} else {
columns_.type_ = grn_ctx_get(ctx_, TABLE_NAME "." COLUMN_TYPE_NAME, -1);
columns_.table_ = grn_ctx_get(ctx_, TABLE_NAME "." COLUMN_TABLE_NAME, -1);
columns_.record_ = grn_ctx_get(ctx_, TABLE_NAME "." COLUMN_RECORD_NAME, -1);
}
is_enabled_recording_ = true;
DBUG_VOID_RETURN;
}
Operations::~Operations() {
MRN_DBUG_ENTER_METHOD();
GRN_OBJ_FIN(ctx_, &id_buffer_);
GRN_OBJ_FIN(ctx_, &text_buffer_);
DBUG_VOID_RETURN;
}
bool Operations::is_locked() {
MRN_DBUG_ENTER_METHOD();
if (grn_obj_is_locked(ctx_, table_) > 0)
DBUG_RETURN(true);
if (grn_obj_is_locked(ctx_, columns_.type_) > 0)
DBUG_RETURN(true);
if (grn_obj_is_locked(ctx_, columns_.table_) > 0)
DBUG_RETURN(true);
if (grn_obj_is_locked(ctx_, columns_.record_) > 0)
DBUG_RETURN(true);
DBUG_RETURN(false);
}
grn_id Operations::start(const char *type,
const char *table_name, size_t table_name_size) {
MRN_DBUG_ENTER_METHOD();
if (!is_enabled_recording_) {
DBUG_RETURN(GRN_ID_NIL);
}
grn_id id = grn_table_add(ctx_, table_, NULL, 0, NULL);
GRN_TEXT_SETS(ctx_, &text_buffer_, type);
grn_obj_set_value(ctx_, columns_.type_, id, &text_buffer_, GRN_OBJ_SET);
GRN_TEXT_SET(ctx_, &text_buffer_, table_name, table_name_size);
grn_obj_set_value(ctx_, columns_.table_, id, &text_buffer_, GRN_OBJ_SET);
DBUG_RETURN(id);
}
void Operations::record_target(grn_id id, grn_id record_id) {
MRN_DBUG_ENTER_METHOD();
if (!is_enabled_recording_) {
DBUG_VOID_RETURN;
}
GRN_UINT32_SET(ctx_, &id_buffer_, record_id);
grn_obj_set_value(ctx_, columns_.record_, id, &id_buffer_, GRN_OBJ_SET);
DBUG_VOID_RETURN;
}
void Operations::finish(grn_id id) {
MRN_DBUG_ENTER_METHOD();
if (!is_enabled_recording_) {
DBUG_VOID_RETURN;
}
grn_table_delete_by_id(ctx_, table_, id);
DBUG_VOID_RETURN;
}
void Operations::enable_recording() {
MRN_DBUG_ENTER_METHOD();
is_enabled_recording_ = true;
DBUG_VOID_RETURN;
}
void Operations::disable_recording() {
MRN_DBUG_ENTER_METHOD();
is_enabled_recording_ = false;
DBUG_VOID_RETURN;
}
grn_hash *Operations::collect_processing_table_names() {
MRN_DBUG_ENTER_METHOD();
grn_hash *table_names =
grn_hash_create(ctx_, NULL, GRN_TABLE_MAX_KEY_SIZE, 0,
GRN_OBJ_TABLE_HASH_KEY | GRN_OBJ_KEY_VAR_SIZE);
grn_table_cursor *cursor;
cursor = grn_table_cursor_open(ctx_, table_, NULL, 0, NULL, 0, 0, -1, 0);
if (!cursor) {
GRN_LOG(ctx_, GRN_LOG_NOTICE,
"[operations] failed to open cursor: %s",
ctx_->errbuf);
DBUG_RETURN(table_names);
}
grn_id id;
while ((id = grn_table_cursor_next(ctx_, cursor))) {
GRN_BULK_REWIND(&text_buffer_);
grn_obj_get_value(ctx_, columns_.table_, id, &text_buffer_);
if (GRN_TEXT_LEN(&text_buffer_) > 0) {
grn_hash_add(ctx_, table_names,
GRN_TEXT_VALUE(&text_buffer_),
GRN_TEXT_LEN(&text_buffer_),
NULL,
NULL);
}
}
grn_table_cursor_close(ctx_, cursor);
DBUG_RETURN(table_names);
}
int Operations::repair(const char *table_name, size_t table_name_size) {
MRN_DBUG_ENTER_METHOD();
int error = 0;
grn_table_cursor *cursor;
cursor = grn_table_cursor_open(ctx_, table_, NULL, 0, NULL, 0, 0, -1, 0);
if (!cursor) {
error = HA_ERR_CRASHED_ON_USAGE;
if (ctx_->rc) {
my_message(error, ctx_->errbuf, MYF(0));
} else {
my_message(error,
"mroonga: repair: "
"failed to open cursor for operations table",
MYF(0));
}
DBUG_RETURN(error);
}
grn_obj *target_table = grn_ctx_get(ctx_, table_name, table_name_size);
if (!target_table) {
GRN_LOG(ctx_, GRN_LOG_WARNING,
"table doesn't exist for auto repair: <%.*s>",
static_cast<int>(table_name_size), table_name);
}
grn_id id;
while ((id = grn_table_cursor_next(ctx_, cursor))) {
GRN_BULK_REWIND(&text_buffer_);
grn_obj_get_value(ctx_, columns_.table_, id, &text_buffer_);
if (!((static_cast<size_t>(GRN_TEXT_LEN(&text_buffer_)) ==
table_name_size) &&
memcmp(GRN_TEXT_VALUE(&text_buffer_),
table_name,
table_name_size) == 0)) {
continue;
}
if (!target_table) {
grn_rc rc = grn_table_cursor_delete(ctx_, cursor);
if (rc != GRN_SUCCESS) {
GRN_BULK_REWIND(&text_buffer_);
grn_obj_get_value(ctx_, columns_.type_, id, &text_buffer_);
GRN_TEXT_PUTC(ctx_, &text_buffer_, '\0');
char error_message[MRN_MESSAGE_BUFFER_SIZE];
snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
"mroonga: repair: failed to delete an orphan operation: "
"[%u]: <%.*s>[%s]: <%s>(%d)",
id,
static_cast<int>(table_name_size), table_name,
GRN_TEXT_VALUE(&text_buffer_),
ctx_->errbuf,
rc);
my_message(error, error_message, MYF(0));
break;
}
continue;
}
GRN_BULK_REWIND(&id_buffer_);
grn_obj_get_value(ctx_, columns_.record_, id, &id_buffer_);
grn_id record_id = GRN_UINT32_VALUE(&id_buffer_);
if (record_id == GRN_ID_NIL) {
grn_rc rc = grn_table_cursor_delete(ctx_, cursor);
if (rc != GRN_SUCCESS) {
GRN_BULK_REWIND(&text_buffer_);
grn_obj_get_value(ctx_, columns_.type_, id, &text_buffer_);
GRN_TEXT_PUTC(ctx_, &text_buffer_, '\0');
char error_message[MRN_MESSAGE_BUFFER_SIZE];
snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
"mroonga: repair: "
"failed to delete an operation that has no related record: "
"[%u]: <%.*s>[%s]: <%s>(%d)",
id,
static_cast<int>(table_name_size), table_name,
GRN_TEXT_VALUE(&text_buffer_),
ctx_->errbuf,
rc);
my_message(error, error_message, MYF(0));
break;
}
continue;
}
GRN_BULK_REWIND(&text_buffer_);
grn_obj_get_value(ctx_, columns_.type_, id, &text_buffer_);
GRN_TEXT_PUTC(ctx_, &text_buffer_, '\0');
if (strcmp(GRN_TEXT_VALUE(&text_buffer_), "write") == 0 ||
strcmp(GRN_TEXT_VALUE(&text_buffer_), "delete") == 0) {
grn_rc rc = grn_table_delete_by_id(ctx_, target_table, record_id);
if (rc != GRN_SUCCESS) {
error = HA_ERR_CRASHED_ON_USAGE;
char error_message[MRN_MESSAGE_BUFFER_SIZE];
snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
"mroonga: repair: failed to delete an incomplete record: "
"[%u]: <%.*s>[%u]: <%s>(%d)",
id,
static_cast<int>(table_name_size), table_name,
record_id,
ctx_->errbuf,
rc);
my_message(error, error_message, MYF(0));
break;
}
rc = grn_table_cursor_delete(ctx_, cursor);
if (rc != GRN_SUCCESS) {
error = HA_ERR_CRASHED_ON_USAGE;
char error_message[MRN_MESSAGE_BUFFER_SIZE];
snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
"mroonga: repair: failed to delete an incomplete operation: "
"[%u]: <%.*s>[%u][%s]: <%s>(%d)",
id,
static_cast<int>(table_name_size), table_name,
record_id,
GRN_TEXT_VALUE(&text_buffer_),
ctx_->errbuf,
rc);
my_message(error, error_message, MYF(0));
break;
}
} else if (strcmp(GRN_TEXT_VALUE(&text_buffer_), "update") == 0) {
error = HA_ERR_CRASHED_ON_USAGE;
my_message(error,
"mroonga: repair: can't recover from crash while updating",
MYF(0));
break;
} else {
error = HA_ERR_CRASHED_ON_USAGE;
char error_message[MRN_MESSAGE_BUFFER_SIZE];
snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
"mroonga: repair: unknown operation type: "
"[%u]: <%.*s>[%u]: <%s>",
id,
static_cast<int>(table_name_size), table_name,
record_id,
GRN_TEXT_VALUE(&text_buffer_));
my_message(error, error_message, MYF(0));
break;
}
}
grn_table_cursor_close(ctx_, cursor);
DBUG_RETURN(error);
}
int Operations::clear(const char *table_name, size_t table_name_size) {
MRN_DBUG_ENTER_METHOD();
int error = 0;
grn_table_cursor *cursor;
cursor = grn_table_cursor_open(ctx_, table_, NULL, 0, NULL, 0, 0, -1, 0);
if (!cursor) {
error = HA_ERR_CRASHED_ON_USAGE;
if (ctx_->rc) {
my_message(error, ctx_->errbuf, MYF(0));
} else {
my_message(error,
"mroonga: clear: "
"failed to open cursor for operations table",
MYF(0));
}
DBUG_RETURN(error);
}
grn_id id;
while ((id = grn_table_cursor_next(ctx_, cursor))) {
GRN_BULK_REWIND(&text_buffer_);
grn_obj_get_value(ctx_, columns_.table_, id, &text_buffer_);
if ((static_cast<size_t>(GRN_TEXT_LEN(&text_buffer_)) ==
table_name_size) &&
memcmp(GRN_TEXT_VALUE(&text_buffer_),
table_name,
table_name_size) == 0) {
grn_rc rc = grn_table_cursor_delete(ctx_, cursor);
if (rc != GRN_SUCCESS) {
error = HA_ERR_CRASHED_ON_USAGE;
GRN_BULK_REWIND(&id_buffer_);
grn_obj_get_value(ctx_, columns_.record_, id, &id_buffer_);
GRN_BULK_REWIND(&text_buffer_);
grn_obj_get_value(ctx_, columns_.type_, id, &text_buffer_);
GRN_TEXT_PUTC(ctx_, &text_buffer_, '\0');
char error_message[MRN_MESSAGE_BUFFER_SIZE];
snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
"mroonga: clear: failed to delete an operation: "
"[%u]: <%.*s>[%u][%s]: <%s>(%d)",
id,
static_cast<int>(table_name_size), table_name,
GRN_UINT32_VALUE(&id_buffer_),
GRN_TEXT_VALUE(&text_buffer_),
ctx_->errbuf,
rc);
my_message(error, error_message, MYF(0));
break;
}
}
}
grn_table_cursor_close(ctx_, cursor);
DBUG_RETURN(error);
}
}

View file

@ -0,0 +1,60 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2015 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
*/
#ifndef MRN_OPERATIONS_HPP_
#define MRN_OPERATIONS_HPP_
#include <groonga.h>
namespace mrn {
class Operations {
public:
Operations(grn_ctx *ctx);
~Operations();
bool is_locked();
grn_id start(const char *type,
const char *table_name, size_t table_name_size);
void record_target(grn_id id, grn_id target_id);
void finish(grn_id id);
void enable_recording();
void disable_recording();
grn_hash *collect_processing_table_names();
int repair(const char *table_name, size_t table_name_size);
int clear(const char *table_name, size_t table_name_size);
private:
grn_ctx *ctx_;
grn_obj text_buffer_;
grn_obj id_buffer_;
grn_obj *table_;
struct {
grn_obj *type_;
grn_obj *table_;
grn_obj *record_;
} columns_;
bool is_enabled_recording_;
};
}
#endif /* MRN_OPERATIONS_HPP_ */

View file

@ -222,4 +222,8 @@ namespace mrn {
mysql_path_[i] = '\0';
return mysql_path_;
}
bool PathMapper::is_internal_table_name() {
return mysql_table_name()[0] == '#';
}
}

View file

@ -38,6 +38,8 @@ namespace mrn {
const char *table_name();
const char *mysql_table_name();
const char *mysql_path();
bool is_internal_table_name();
bool is_temporary_table_name();
private:
const char *original_mysql_path_;
const char *path_prefix_;

View file

@ -0,0 +1,361 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2017 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
*/
#include "mrn_query_parser.hpp"
#include <mrn_variables.hpp>
extern "C" {
/* Groonga's internal functions */
int grn_atoi(const char *nptr, const char *end, const char **rest);
uint grn_atoui(const char *nptr, const char *end, const char **rest);
}
#define MRN_CLASS_NAME "mrn::QueryParser"
namespace mrn {
QueryParser::QueryParser(grn_ctx *ctx,
THD *thd,
grn_obj *expression,
grn_obj *default_column,
uint n_sections,
grn_obj *match_columns)
: ctx_(ctx),
thd_(thd),
expression_(expression),
default_column_(default_column),
n_sections_(n_sections),
match_columns_(match_columns) {
}
QueryParser::~QueryParser() {
}
grn_rc QueryParser::parse(const char *query, size_t query_length) {
MRN_DBUG_ENTER_METHOD();
const char *raw_query = NULL;
size_t raw_query_length = 0;
grn_operator default_operator = GRN_OP_OR;
grn_expr_flags expression_flags = 0;
parse_pragma(query,
query_length,
&raw_query,
&raw_query_length,
&default_operator,
&expression_flags);
grn_obj *default_column = default_column_;
if (match_columns_) {
default_column = match_columns_;
}
grn_rc rc = grn_expr_parse(ctx_,
expression_,
raw_query,
raw_query_length,
default_column,
GRN_OP_MATCH,
default_operator,
expression_flags);
if (rc != GRN_SUCCESS) {
char error_message[MRN_MESSAGE_BUFFER_SIZE];
snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
"failed to parse fulltext search keyword: <%.*s>: <%s>",
static_cast<int>(query_length),
query,
ctx_->errbuf);
variables::ActionOnError action =
variables::get_action_on_fulltext_query_error(thd_);
switch (action) {
case variables::ACTION_ON_ERROR_ERROR:
my_message(ER_PARSE_ERROR, error_message, MYF(0));
break;
case variables::ACTION_ON_ERROR_ERROR_AND_LOG:
my_message(ER_PARSE_ERROR, error_message, MYF(0));
GRN_LOG(ctx_, GRN_LOG_ERROR, "%s", error_message);
break;
case variables::ACTION_ON_ERROR_IGNORE:
break;
case variables::ACTION_ON_ERROR_IGNORE_AND_LOG:
GRN_LOG(ctx_, GRN_LOG_ERROR, "%s", error_message);
break;
}
}
DBUG_RETURN(rc);
}
void QueryParser::parse_pragma(const char *query,
size_t query_length,
const char **raw_query,
size_t *raw_query_length,
grn_operator *default_operator,
grn_expr_flags *flags) {
MRN_DBUG_ENTER_METHOD();
const char *current_query = query;
size_t current_query_length = query_length;
*default_operator = GRN_OP_OR;
if (current_query_length >= 4 && memcmp(current_query, "*SS ", 4) == 0) {
*raw_query = current_query + 4;
*raw_query_length = current_query_length - 4;
*flags = GRN_EXPR_SYNTAX_SCRIPT;
DBUG_VOID_RETURN;
}
bool weight_specified = false;
*raw_query = query;
*raw_query_length = query_length;
*flags = default_expression_flags();
if (current_query_length >= 2 && current_query[0] == '*') {
bool parsed = false;
bool done = false;
current_query++;
current_query_length--;
while (!done) {
size_t consumed_query_length = 0;
switch (current_query[0]) {
case 'D':
if (parse_pragma_d(current_query + 1,
current_query_length - 1,
default_operator,
&consumed_query_length)) {
parsed = true;
consumed_query_length += 1;
current_query += consumed_query_length;
current_query_length -= consumed_query_length;
} else {
done = true;
}
break;
case 'W':
if (parse_pragma_w(current_query + 1,
current_query_length - 1,
&consumed_query_length)) {
parsed = true;
weight_specified = true;
consumed_query_length += 1;
current_query += consumed_query_length;
current_query_length -= consumed_query_length;
} else {
done = true;
}
break;
default:
done = true;
break;
}
}
if (parsed) {
*raw_query = current_query;
*raw_query_length = current_query_length;
}
}
// WORKAROUND: ignore the first '+' to support "+apple macintosh" pattern.
while (*raw_query_length > 0 && (*raw_query)[0] == ' ') {
(*raw_query)++;
(*raw_query_length)--;
}
if (*raw_query_length > 0 && (*raw_query)[0] == '+') {
(*raw_query)++;
(*raw_query_length)--;
}
if (!weight_specified && match_columns_) {
grn_expr_append_obj(ctx_, match_columns_, default_column_, GRN_OP_PUSH, 1);
}
DBUG_VOID_RETURN;
}
bool QueryParser::parse_pragma_w(const char *query,
size_t query_length,
size_t *consumed_query_length) {
MRN_DBUG_ENTER_METHOD();
*consumed_query_length = 0;
grn_obj section_value_buffer;
GRN_UINT32_INIT(&section_value_buffer, 0);
MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(bool, specified_sections, n_sections_);
for (uint i = 0; i < n_sections_; ++i) {
specified_sections[i] = false;
}
uint n_weights = 0;
while (query_length >= 1) {
if (n_weights >= 1) {
if (query[0] != ',') {
break;
}
size_t n_used_query_length = 1;
*consumed_query_length += n_used_query_length;
query_length -= n_used_query_length;
query += n_used_query_length;
if (query_length == 0) {
break;
}
}
uint section = 0;
if ('1' <= query[0] && query[0] <= '9') {
const char *section_start = query;
const char *query_end = query + query_length;
const char *query_rest;
section = grn_atoui(section_start, query_end, &query_rest);
if (section_start == query_rest) {
break;
}
if (!(0 < section && section <= n_sections_)) {
break;
}
section -= 1;
specified_sections[section] = true;
size_t n_used_query_length = query_rest - query;
*consumed_query_length += n_used_query_length;
query_length -= n_used_query_length;
query += n_used_query_length;
} else {
break;
}
int weight = 1;
if (query_length >= 2 && query[0] == ':') {
const char *weight_start = query + 1;
const char *query_end = query + query_length;
const char *query_rest;
weight = grn_atoi(weight_start, query_end, &query_rest);
if (weight_start == query_rest) {
break;
}
size_t n_used_query_length = query_rest - query;
*consumed_query_length += n_used_query_length;
query_length -= n_used_query_length;
query += n_used_query_length;
}
n_weights++;
append_section(section,
&section_value_buffer,
weight,
n_weights);
}
for (uint section = 0; section < n_sections_; ++section) {
if (specified_sections[section]) {
continue;
}
++n_weights;
int default_weight = 1;
append_section(section,
&section_value_buffer,
default_weight,
n_weights);
}
MRN_FREE_VARIABLE_LENGTH_ARRAYS(specified_sections);
GRN_OBJ_FIN(ctx_, &section_value_buffer);
DBUG_RETURN(n_weights > 0);
}
void QueryParser::append_section(uint section,
grn_obj *section_value_buffer,
int weight,
uint n_weights) {
MRN_DBUG_ENTER_METHOD();
if (!match_columns_) {
DBUG_VOID_RETURN;
}
grn_expr_append_obj(ctx_, match_columns_, default_column_, GRN_OP_PUSH, 1);
GRN_UINT32_SET(ctx_, section_value_buffer, section);
grn_expr_append_const(ctx_, match_columns_, section_value_buffer,
GRN_OP_PUSH, 1);
grn_expr_append_op(ctx_, match_columns_, GRN_OP_GET_MEMBER, 2);
if (weight != 1) {
grn_expr_append_const_int(ctx_, match_columns_, weight, GRN_OP_PUSH, 1);
grn_expr_append_op(ctx_, match_columns_, GRN_OP_STAR, 2);
}
if (n_weights >= 2) {
grn_expr_append_op(ctx_, match_columns_, GRN_OP_OR, 2);
}
DBUG_VOID_RETURN;
}
bool QueryParser::parse_pragma_d(const char *query,
size_t query_length,
grn_operator *default_operator,
size_t *consumed_query_length) {
MRN_DBUG_ENTER_METHOD();
bool succeeded = true;
if (query_length >= 1 && query[0] == '+') {
*default_operator = GRN_OP_AND;
*consumed_query_length = 1;
} else if (query_length >= 1 && query[0] == '-') {
*default_operator = GRN_OP_AND_NOT;
*consumed_query_length = 1;
} else if (query_length >= 2 && memcmp(query, "OR", 2) == 0) {
*default_operator = GRN_OP_OR;
*consumed_query_length = 2;
} else {
succeeded = false;
}
DBUG_RETURN(succeeded);
}
grn_expr_flags QueryParser::default_expression_flags() {
MRN_DBUG_ENTER_METHOD();
ulonglong syntax_flags = variables::get_boolean_mode_syntax_flags(thd_);
grn_expr_flags expression_flags = 0;
if (syntax_flags == variables::BOOLEAN_MODE_SYNTAX_FLAG_DEFAULT) {
expression_flags = GRN_EXPR_SYNTAX_QUERY | GRN_EXPR_ALLOW_LEADING_NOT;
} else {
if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_SYNTAX_SCRIPT) {
expression_flags |= GRN_EXPR_SYNTAX_SCRIPT;
} else {
expression_flags |= GRN_EXPR_SYNTAX_QUERY;
}
if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_ALLOW_COLUMN) {
expression_flags |= GRN_EXPR_ALLOW_COLUMN;
}
if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_ALLOW_UPDATE) {
expression_flags |= GRN_EXPR_ALLOW_UPDATE;
}
if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_ALLOW_LEADING_NOT) {
expression_flags |= GRN_EXPR_ALLOW_LEADING_NOT;
}
}
DBUG_RETURN(expression_flags);
}
}

View file

@ -0,0 +1,67 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2017 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
*/
#pragma once
#include <mrn_mysql.h>
#include <mrn_mysql_compat.h>
#include <groonga.h>
namespace mrn {
class QueryParser {
public:
QueryParser(grn_ctx *ctx,
THD *thd,
grn_obj *expression,
grn_obj *default_column,
uint n_sections,
grn_obj *match_columns=NULL);
~QueryParser();
grn_rc parse(const char *query, size_t query_length);
void parse_pragma(const char *query,
size_t query_length,
const char **raw_query,
size_t *raw_query_length,
grn_operator *default_operator,
grn_expr_flags *flags);
private:
grn_ctx *ctx_;
THD *thd_;
grn_obj *expression_;
grn_obj *default_column_;
uint n_sections_;
grn_obj *match_columns_;
bool parse_pragma_w(const char *query,
size_t query_length,
size_t *consumed_query_length);
void append_section(uint section,
grn_obj *section_value_buffer,
int weight,
uint n_weights);
bool parse_pragma_d(const char *query,
size_t query_length,
grn_operator *default_operator,
size_t *consumed_query_length);
grn_expr_flags default_expression_flags();
};
}

View file

@ -0,0 +1,42 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2017 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
*/
#include "mrn_smart_bitmap.hpp"
namespace mrn {
SmartBitmap::SmartBitmap(MY_BITMAP *bitmap)
: bitmap_(bitmap) {
}
SmartBitmap::~SmartBitmap() {
if (bitmap_) {
bitmap_free(bitmap_);
}
}
MY_BITMAP *SmartBitmap::get() {
return bitmap_;
}
MY_BITMAP *SmartBitmap::release() {
MY_BITMAP *bitmap = bitmap_;
bitmap_ = NULL;
return bitmap;
}
}

View file

@ -0,0 +1,36 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2017 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
*/
#pragma once
#include <mrn_mysql.h>
#include <my_bitmap.h>
namespace mrn {
class SmartBitmap {
public:
SmartBitmap(MY_BITMAP *bitmap);
~SmartBitmap();
MY_BITMAP *get();
MY_BITMAP *release();
private:
MY_BITMAP *bitmap_;
};
}

View file

@ -0,0 +1,41 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2017 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
*/
#include "mrn_table_fields_offset_mover.hpp"
namespace mrn {
TableFieldsOffsetMover::TableFieldsOffsetMover(TABLE *table,
my_ptrdiff_t diff)
: table_(table),
diff_(diff) {
uint n_columns = table_->s->fields;
for (uint i = 0; i < n_columns; ++i) {
Field *field = table_->field[i];
field->move_field_offset(diff_);
}
}
TableFieldsOffsetMover::~TableFieldsOffsetMover() {
uint n_columns = table_->s->fields;
for (uint i = 0; i < n_columns; ++i) {
Field *field = table_->field[i];
field->move_field_offset(-diff_);
}
}
}

View file

@ -0,0 +1,33 @@
/* -*- c-basic-offset: 2 -*- */
/*
Copyright(C) 2017 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
*/
#pragma once
#include <mrn_mysql.h>
namespace mrn {
class TableFieldsOffsetMover {
public:
TableFieldsOffsetMover(TABLE *table, my_ptrdiff_t diff);
~TableFieldsOffsetMover();
private:
TABLE *table_;
my_ptrdiff_t diff_;
};
}