diff options
Diffstat (limited to 'storage/mroonga/udf')
-rw-r--r-- | storage/mroonga/udf/mrn_udf_command.cpp | 194 | ||||
-rw-r--r-- | storage/mroonga/udf/mrn_udf_escape.cpp | 196 | ||||
-rw-r--r-- | storage/mroonga/udf/mrn_udf_highlight_html.cpp | 494 | ||||
-rw-r--r-- | storage/mroonga/udf/mrn_udf_last_insert_grn_id.cpp | 11 | ||||
-rw-r--r-- | storage/mroonga/udf/mrn_udf_normalize.cpp | 212 | ||||
-rw-r--r-- | storage/mroonga/udf/mrn_udf_query_expand.cpp | 282 | ||||
-rw-r--r-- | storage/mroonga/udf/mrn_udf_snippet.cpp | 77 | ||||
-rw-r--r-- | storage/mroonga/udf/mrn_udf_snippet_html.cpp | 444 | ||||
-rw-r--r-- | storage/mroonga/udf/sources.am | 6 |
9 files changed, 1800 insertions, 116 deletions
diff --git a/storage/mroonga/udf/mrn_udf_command.cpp b/storage/mroonga/udf/mrn_udf_command.cpp index d14f3ffd49d..b4d0f8b20a3 100644 --- a/storage/mroonga/udf/mrn_udf_command.cpp +++ b/storage/mroonga/udf/mrn_udf_command.cpp @@ -2,7 +2,7 @@ /* Copyright(C) 2010 Tetsuro IKEDA Copyright(C) 2010-2013 Kentoku SHIBA - Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com> + 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 @@ -25,39 +25,93 @@ #include <mrn_windows.hpp> #include <mrn_macro.hpp> #include <mrn_database_manager.hpp> +#include <mrn_context_pool.hpp> #include <mrn_variables.hpp> +#include <mrn_current_thread.hpp> + +#include <sql_table.h> MRN_BEGIN_DECLS extern mrn::DatabaseManager *mrn_db_manager; +extern mrn::ContextPool *mrn_context_pool; struct CommandInfo { - grn_ctx ctx; + grn_ctx *ctx; grn_obj *db; bool use_shared_db; + grn_obj command; String result; }; -MRN_API my_bool mroonga_command_init(UDF_INIT *initid, UDF_ARGS *args, +MRN_API my_bool mroonga_command_init(UDF_INIT *init, UDF_ARGS *args, char *message) { CommandInfo *info = NULL; - initid->ptr = NULL; - if (args->arg_count != 1) { - sprintf(message, - "mroonga_command(): Incorrect number of arguments: %u for 1", - args->arg_count); + init->ptr = NULL; + if (args->arg_count == 0) { + grn_snprintf(message, + MYSQL_ERRMSG_SIZE, + MYSQL_ERRMSG_SIZE, + "mroonga_command(): Wrong number of arguments: %u for 1..", + args->arg_count); goto error; } - if (args->arg_type[0] != STRING_RESULT) { - strcpy(message, - "mroonga_command(): The 1st argument must be command as string"); + + if ((args->arg_count % 2) == 0) { + grn_snprintf(message, + MYSQL_ERRMSG_SIZE, + MYSQL_ERRMSG_SIZE, + "mroonga_command(): The number of arguments must be odd: %u", + args->arg_count); goto error; } - initid->maybe_null = 1; - initid->const_item = 1; + + for (unsigned int i = 0; i < args->arg_count; ++i) { + switch (args->arg_type[i]) { + case STRING_RESULT: + // OK + break; + case REAL_RESULT: + grn_snprintf(message, + MYSQL_ERRMSG_SIZE, + MYSQL_ERRMSG_SIZE, + "mroonga_command(): Argument must be string: <%g>", + *reinterpret_cast<double *>(args->args[i])); + goto error; + break; + case INT_RESULT: + grn_snprintf(message, + MYSQL_ERRMSG_SIZE, + MYSQL_ERRMSG_SIZE, + "mroonga_command(): Argument must be string: <%lld>", + *reinterpret_cast<longlong *>(args->args[i])); + goto error; + break; + case DECIMAL_RESULT: + grn_snprintf(message, + MYSQL_ERRMSG_SIZE, + MYSQL_ERRMSG_SIZE, + "mroonga_command(): Argument must be string: <%.*s>", + static_cast<int>(args->lengths[i]), + args->args[i]); + goto error; + break; + default: + grn_snprintf(message, + MYSQL_ERRMSG_SIZE, + MYSQL_ERRMSG_SIZE, + "mroonga_command(): Argument must be string: <%d>(%u)", + args->arg_type[i], + i); + goto error; + break; + } + } + init->maybe_null = 1; + init->const_item = 0; info = (CommandInfo *)mrn_my_malloc(sizeof(CommandInfo), MYF(MY_WME | MY_ZEROFILL)); @@ -66,53 +120,100 @@ MRN_API my_bool mroonga_command_init(UDF_INIT *initid, UDF_ARGS *args, goto error; } - grn_ctx_init(&(info->ctx), 0); + info->ctx = mrn_context_pool->pull(); { const char *current_db_path = MRN_THD_DB_PATH(current_thd); const char *action; if (current_db_path) { action = "open database"; - int error = mrn_db_manager->open(current_db_path, &(info->db)); + char encoded_db_path[FN_REFLEN + 1]; + uint encoded_db_path_length = + tablename_to_filename(current_db_path, + encoded_db_path, + sizeof(encoded_db_path)); + encoded_db_path[encoded_db_path_length] = '\0'; + mrn::Database *db; + int error = mrn_db_manager->open(encoded_db_path, &db); if (error == 0) { - grn_ctx_use(&(info->ctx), info->db); + info->db = db->get(); + grn_ctx_use(info->ctx, info->db); info->use_shared_db = true; } } else { action = "create anonymous database"; - info->db = grn_db_create(&(info->ctx), NULL, NULL); + info->db = grn_db_create(info->ctx, NULL, NULL); info->use_shared_db = false; } if (!info->db) { - sprintf(message, - "mroonga_command(): failed to %s: %s", - action, - info->ctx.errbuf); + grn_snprintf(message, + MYSQL_ERRMSG_SIZE, + MYSQL_ERRMSG_SIZE, + "mroonga_command(): failed to %s: %s", + action, + info->ctx->errbuf); goto error; } } + GRN_TEXT_INIT(&(info->command), 0); - initid->ptr = (char *)info; + init->ptr = (char *)info; return FALSE; error: if (info) { if (!info->use_shared_db) { - grn_obj_close(&(info->ctx), info->db); + grn_obj_close(info->ctx, info->db); } - grn_ctx_fin(&(info->ctx)); + mrn_context_pool->release(info->ctx); my_free(info); } return TRUE; } -MRN_API char *mroonga_command(UDF_INIT *initid, UDF_ARGS *args, char *result, +static void mroonga_command_escape_value(grn_ctx *ctx, + grn_obj *command, + const char *value, + unsigned long value_length) +{ + GRN_TEXT_PUTC(ctx, command, '"'); + + const char *value_current = value; + const char *value_end = value_current + value_length; + while (value_current < value_end) { + int char_length = grn_charlen(ctx, value_current, value_end); + + if (char_length == 0) { + break; + } else if (char_length == 1) { + switch (*value_current) { + case '\\': + case '"': + GRN_TEXT_PUTC(ctx, command, '\\'); + GRN_TEXT_PUTC(ctx, command, *value_current); + break; + case '\n': + GRN_TEXT_PUTS(ctx, command, "\\n"); + break; + default: + GRN_TEXT_PUTC(ctx, command, *value_current); + break; + } + } else { + GRN_TEXT_PUT(ctx, command, value_current, char_length); + } + + value_current += char_length; + } + + GRN_TEXT_PUTC(ctx, command, '"'); +} + +MRN_API char *mroonga_command(UDF_INIT *init, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error) { - CommandInfo *info = (CommandInfo *)initid->ptr; - grn_ctx *ctx = &(info->ctx); - char *command; - unsigned int command_length; + CommandInfo *info = (CommandInfo *)init->ptr; + grn_ctx *ctx = info->ctx; int flags = 0; if (!args->args[0]) { @@ -120,11 +221,31 @@ MRN_API char *mroonga_command(UDF_INIT *initid, UDF_ARGS *args, char *result, return NULL; } + GRN_BULK_REWIND(&(info->command)); + GRN_TEXT_PUT(ctx, &(info->command), args->args[0], args->lengths[0]); + for (unsigned int i = 1; i < args->arg_count; i += 2) { + if (!args->args[i] || !args->args[i + 1]) { + *is_null = 1; + return NULL; + } + + const char *name = args->args[i]; + unsigned long name_length = args->lengths[i]; + GRN_TEXT_PUTS(ctx, &(info->command), " --"); + GRN_TEXT_PUT(ctx, &(info->command), name, name_length); + + const char *value = args->args[i + 1]; + unsigned long value_length = args->lengths[i + 1]; + GRN_TEXT_PUTS(ctx, &(info->command), " "); + mroonga_command_escape_value(ctx, &(info->command), value, value_length); + } + *is_null = 0; - command = args->args[0]; - command_length = args->lengths[0]; - grn_ctx_send(ctx, command, command_length, 0); + grn_ctx_send(ctx, + GRN_TEXT_VALUE(&(info->command)), + GRN_TEXT_LEN(&(info->command)), + 0); if (ctx->rc) { my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0)); goto error; @@ -156,14 +277,15 @@ error: return NULL; } -MRN_API void mroonga_command_deinit(UDF_INIT *initid) +MRN_API void mroonga_command_deinit(UDF_INIT *init) { - CommandInfo *info = (CommandInfo *)initid->ptr; + CommandInfo *info = (CommandInfo *)init->ptr; if (info) { + GRN_OBJ_FIN(info->ctx, &(info->command)); if (!info->use_shared_db) { - grn_obj_close(&(info->ctx), info->db); + grn_obj_close(info->ctx, info->db); } - grn_ctx_fin(&(info->ctx)); + mrn_context_pool->release(info->ctx); MRN_STRING_FREE(info->result); my_free(info); } diff --git a/storage/mroonga/udf/mrn_udf_escape.cpp b/storage/mroonga/udf/mrn_udf_escape.cpp index 89b08a1c6d5..b97327fb1d0 100644 --- a/storage/mroonga/udf/mrn_udf_escape.cpp +++ b/storage/mroonga/udf/mrn_udf_escape.cpp @@ -1,6 +1,6 @@ /* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* - Copyright(C) 2013-2015 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 @@ -23,33 +23,56 @@ #include <mrn_windows.hpp> #include <mrn_macro.hpp> #include <mrn_variables.hpp> +#include <mrn_context_pool.hpp> MRN_BEGIN_DECLS +extern mrn::ContextPool *mrn_context_pool; + struct EscapeInfo { - grn_ctx ctx; + grn_ctx *ctx; + bool script_mode; grn_obj target_characters; - grn_obj escaped_query; - bool processed; + grn_obj escaped_value; }; -MRN_API my_bool mroonga_escape_init(UDF_INIT *initid, UDF_ARGS *args, +MRN_API my_bool mroonga_escape_init(UDF_INIT *init, UDF_ARGS *args, char *message) { EscapeInfo *info = NULL; + bool script_mode = false; - initid->ptr = NULL; + init->ptr = NULL; if (!(1 <= args->arg_count && args->arg_count <= 2)) { - sprintf(message, - "mroonga_escape(): Incorrect number of arguments: %u for 1..2", - args->arg_count); + snprintf(message, + MYSQL_ERRMSG_SIZE, + "mroonga_escape(): Incorrect number of arguments: %u for 1..2", + args->arg_count); goto error; } - if (args->arg_type[0] != STRING_RESULT) { - strcpy(message, - "mroonga_escape(): The 1st argument must be query as string"); - goto error; + + if (args->attribute_lengths[0] == strlen("script") && + strncmp(args->attributes[0], "script", strlen("script")) == 0) { + switch (args->arg_type[0]) { + case ROW_RESULT: + snprintf(message, + MYSQL_ERRMSG_SIZE, + "mroonga_escape(): " + "The 1st script argument must be " + "string, integer or floating point: <row>"); + goto error; + break; + default: + break; + } + script_mode = true; + } else { + if (args->arg_type[0] != STRING_RESULT) { + strcpy(message, + "mroonga_escape(): The 1st query argument must be string"); + goto error; + } } if (args->arg_count == 2) { if (args->arg_type[1] != STRING_RESULT) { @@ -60,28 +83,27 @@ MRN_API my_bool mroonga_escape_init(UDF_INIT *initid, UDF_ARGS *args, } } - initid->maybe_null = 1; - initid->const_item = 1; + init->maybe_null = 1; - info = (EscapeInfo *)mrn_my_malloc(sizeof(EscapeInfo), - MYF(MY_WME | MY_ZEROFILL)); + info = static_cast<EscapeInfo *>(mrn_my_malloc(sizeof(EscapeInfo), + MYF(MY_WME | MY_ZEROFILL))); if (!info) { strcpy(message, "mroonga_escape(): out of memory"); goto error; } - grn_ctx_init(&(info->ctx), 0); + info->ctx = mrn_context_pool->pull(); + info->script_mode = script_mode; GRN_TEXT_INIT(&(info->target_characters), 0); - GRN_TEXT_INIT(&(info->escaped_query), 0); - info->processed = false; + GRN_TEXT_INIT(&(info->escaped_value), 0); - initid->ptr = (char *)info; + init->ptr = reinterpret_cast<char *>(info); return FALSE; error: if (info) { - grn_ctx_fin(&(info->ctx)); + mrn_context_pool->release(info->ctx); my_free(info); } return TRUE; @@ -89,32 +111,103 @@ error: static void escape(EscapeInfo *info, UDF_ARGS *args) { - grn_ctx *ctx = &(info->ctx); - char *query = args->args[0]; - unsigned int query_length = args->lengths[0]; - - if (args->arg_count == 2) { - char *target_characters = args->args[1]; - unsigned int target_characters_length = args->lengths[1]; - GRN_TEXT_PUT(ctx, &(info->target_characters), - target_characters, - target_characters_length); - GRN_TEXT_PUTC(ctx, &(info->target_characters), '\0'); - grn_expr_syntax_escape(ctx, query, query_length, - GRN_TEXT_VALUE(&(info->target_characters)), - GRN_QUERY_ESCAPE, - &(info->escaped_query)); + grn_ctx *ctx = info->ctx; + + GRN_BULK_REWIND(&(info->escaped_value)); + if (info->script_mode) { + switch (args->arg_type[0]) { + case STRING_RESULT: + { + char *value = args->args[0]; + unsigned long value_length = args->lengths[0]; + GRN_TEXT_PUTC(ctx, &(info->escaped_value), '"'); + if (args->arg_count == 2) { + grn_obj special_characters; + GRN_TEXT_INIT(&special_characters, 0); + GRN_TEXT_PUT(ctx, + &special_characters, + args->args[1], + args->lengths[1]); + GRN_TEXT_PUTC(ctx, &special_characters, '\0'); + grn_expr_syntax_escape(ctx, + value, + value_length, + GRN_TEXT_VALUE(&special_characters), + '\\', + &(info->escaped_value)); + GRN_OBJ_FIN(ctx, &special_characters); + } else { + const char *special_characters = "\"\\"; + grn_expr_syntax_escape(ctx, + value, + value_length, + special_characters, + '\\', + &(info->escaped_value)); + } + GRN_TEXT_PUTC(ctx, &(info->escaped_value), '"'); + } + break; + case REAL_RESULT: + { + double value = *reinterpret_cast<double *>(args->args[0]); + grn_text_ftoa(ctx, &(info->escaped_value), value); + } + break; + case INT_RESULT: + { + longlong value = *reinterpret_cast<longlong *>(args->args[0]); + grn_text_lltoa(ctx, &(info->escaped_value), value); + } + break; + case DECIMAL_RESULT: + { + grn_obj value_raw; + GRN_TEXT_INIT(&value_raw, GRN_OBJ_DO_SHALLOW_COPY); + GRN_TEXT_SET(ctx, &value_raw, args->args[0], args->lengths[0]); + grn_obj value; + GRN_FLOAT_INIT(&value, 0); + if (grn_obj_cast(ctx, &value_raw, &value, GRN_FALSE) == GRN_SUCCESS) { + grn_text_ftoa(ctx, &(info->escaped_value), GRN_FLOAT_VALUE(&value)); + } else { + GRN_TEXT_PUT(ctx, + &(info->escaped_value), + args->args[0], + args->lengths[0]); + } + GRN_OBJ_FIN(ctx, &value); + GRN_OBJ_FIN(ctx, &value_raw); + } + break; + default: + break; + } } else { - grn_expr_syntax_escape_query(ctx, query, query_length, - &(info->escaped_query)); + char *query = args->args[0]; + unsigned long query_length = args->lengths[0]; + if (args->arg_count == 2) { + char *target_characters = args->args[1]; + unsigned long target_characters_length = args->lengths[1]; + GRN_TEXT_PUT(ctx, &(info->target_characters), + target_characters, + target_characters_length); + GRN_TEXT_PUTC(ctx, &(info->target_characters), '\0'); + grn_expr_syntax_escape(ctx, query, query_length, + GRN_TEXT_VALUE(&(info->target_characters)), + GRN_QUERY_ESCAPE, + &(info->escaped_value)); + } else { + grn_expr_syntax_escape_query(ctx, query, query_length, + &(info->escaped_value)); + } } } -MRN_API char *mroonga_escape(UDF_INIT *initid, UDF_ARGS *args, char *result, +MRN_API char *mroonga_escape(UDF_INIT *init, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error) { - EscapeInfo *info = (EscapeInfo *)initid->ptr; - grn_ctx *ctx = &(info->ctx); + EscapeInfo *info = reinterpret_cast<EscapeInfo *>(init->ptr); + grn_ctx *ctx = info->ctx; if (!args->args[0]) { *is_null = 1; @@ -123,31 +216,28 @@ MRN_API char *mroonga_escape(UDF_INIT *initid, UDF_ARGS *args, char *result, *is_null = 0; - if (!info->processed) { - escape(info, args); - info->processed = true; - } + escape(info, args); if (ctx->rc) { my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0)); goto error; } - *length = GRN_TEXT_LEN(&(info->escaped_query)); - return (char *)(GRN_TEXT_VALUE(&(info->escaped_query))); + *length = GRN_TEXT_LEN(&(info->escaped_value)); + return GRN_TEXT_VALUE(&(info->escaped_value)); error: *error = 1; return NULL; } -MRN_API void mroonga_escape_deinit(UDF_INIT *initid) +MRN_API void mroonga_escape_deinit(UDF_INIT *init) { - EscapeInfo *info = (EscapeInfo *)initid->ptr; + EscapeInfo *info = reinterpret_cast<EscapeInfo *>(init->ptr); if (info) { - grn_obj_unlink(&(info->ctx), &(info->target_characters)); - grn_obj_unlink(&(info->ctx), &(info->escaped_query)); - grn_ctx_fin(&(info->ctx)); + grn_obj_unlink(info->ctx, &(info->target_characters)); + grn_obj_unlink(info->ctx, &(info->escaped_value)); + mrn_context_pool->release(info->ctx); my_free(info); } } diff --git a/storage/mroonga/udf/mrn_udf_highlight_html.cpp b/storage/mroonga/udf/mrn_udf_highlight_html.cpp new file mode 100644 index 00000000000..dc46ef5f205 --- /dev/null +++ b/storage/mroonga/udf/mrn_udf_highlight_html.cpp @@ -0,0 +1,494 @@ +/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */ +/* + 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_mysql.h> +#include <mrn_mysql_compat.h> +#include <mrn_err.h> +#include <mrn_encoding.hpp> +#include <mrn_windows.hpp> +#include <mrn_table.hpp> +#include <mrn_macro.hpp> +#include <mrn_database_manager.hpp> +#include <mrn_context_pool.hpp> +#include <mrn_variables.hpp> +#include <mrn_query_parser.hpp> +#include <mrn_current_thread.hpp> + +MRN_BEGIN_DECLS + +extern mrn::DatabaseManager *mrn_db_manager; +extern mrn::ContextPool *mrn_context_pool; + +typedef struct st_mrn_highlight_html_info +{ + grn_ctx *ctx; + grn_obj *db; + bool use_shared_db; + grn_obj *keywords; + String result_str; + struct { + bool used; + grn_obj *table; + grn_obj *default_column; + } query_mode; +} mrn_highlight_html_info; + +static my_bool mrn_highlight_html_prepare(mrn_highlight_html_info *info, + UDF_ARGS *args, + char *message, + grn_obj **keywords) +{ + MRN_DBUG_ENTER_FUNCTION(); + + grn_ctx *ctx = info->ctx; + const char *normalizer_name = "NormalizerAuto"; + grn_obj *expr = NULL; + String *result_str = &(info->result_str); + + *keywords = NULL; + + mrn::encoding::set_raw(ctx, system_charset_info); + if (system_charset_info->state & (MY_CS_BINSORT | MY_CS_CSSORT)) { + normalizer_name = NULL; + } + + *keywords = grn_table_create(ctx, NULL, 0, NULL, + GRN_OBJ_TABLE_PAT_KEY, + grn_ctx_at(ctx, GRN_DB_SHORT_TEXT), + NULL); + if (ctx->rc != GRN_SUCCESS) { + if (message) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_highlight_html(): " + "failed to create grn_pat for keywords: <%s>", + ctx->errbuf); + } + goto error; + } + if (normalizer_name) { + grn_obj_set_info(ctx, + *keywords, + GRN_INFO_NORMALIZER, + grn_ctx_get(ctx, normalizer_name, -1)); + } + + if (info->query_mode.used) { + if (!info->query_mode.table) { + grn_obj *short_text; + short_text = grn_ctx_at(info->ctx, GRN_DB_SHORT_TEXT); + info->query_mode.table = grn_table_create(info->ctx, + NULL, 0, NULL, + GRN_TABLE_HASH_KEY, + short_text, + NULL); + } + if (!info->query_mode.default_column) { + info->query_mode.default_column = + grn_obj_column(info->ctx, + info->query_mode.table, + GRN_COLUMN_NAME_KEY, + GRN_COLUMN_NAME_KEY_LEN); + } + + grn_obj *record = NULL; + GRN_EXPR_CREATE_FOR_QUERY(info->ctx, info->query_mode.table, expr, record); + if (!expr) { + if (message) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_highlight_html(): " + "failed to create expression: <%s>", + ctx->errbuf); + } + goto error; + } + + mrn::QueryParser query_parser(info->ctx, + current_thd, + expr, + info->query_mode.default_column, + 0, + NULL); + grn_rc rc = query_parser.parse(args->args[1], args->lengths[1]); + if (rc != GRN_SUCCESS) { + if (message) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_highlight_html(): " + "failed to parse query: <%s>", + ctx->errbuf); + } + goto error; + } + + { + grn_obj extracted_keywords; + GRN_PTR_INIT(&extracted_keywords, GRN_OBJ_VECTOR, GRN_ID_NIL); + grn_expr_get_keywords(ctx, expr, &extracted_keywords); + + size_t n_keywords = + GRN_BULK_VSIZE(&extracted_keywords) / sizeof(grn_obj *); + for (size_t i = 0; i < n_keywords; ++i) { + grn_obj *extracted_keyword = GRN_PTR_VALUE_AT(&extracted_keywords, i); + grn_table_add(ctx, + *keywords, + GRN_TEXT_VALUE(extracted_keyword), + GRN_TEXT_LEN(extracted_keyword), + NULL); + if (ctx->rc != GRN_SUCCESS) { + if (message) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_highlight_html(): " + "failed to add a keyword: <%.*s>: <%s>", + static_cast<int>(GRN_TEXT_LEN(extracted_keyword)), + GRN_TEXT_VALUE(extracted_keyword), + ctx->errbuf); + GRN_OBJ_FIN(ctx, &extracted_keywords); + } + goto error; + } + } + GRN_OBJ_FIN(ctx, &extracted_keywords); + } + } else { + for (unsigned int i = 1; i < args->arg_count; ++i) { + if (!args->args[i]) { + continue; + } + grn_table_add(ctx, + *keywords, + args->args[i], + args->lengths[i], + NULL); + if (ctx->rc != GRN_SUCCESS) { + if (message) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_highlight_html(): " + "failed to add a keyword: <%.*s>: <%s>", + static_cast<int>(args->lengths[i]), + args->args[i], + ctx->errbuf); + } + goto error; + } + } + } + + result_str->set_charset(system_charset_info); + DBUG_RETURN(FALSE); + +error: + if (expr) { + grn_obj_close(ctx, expr); + } + if (*keywords) { + grn_obj_close(ctx, *keywords); + } + DBUG_RETURN(TRUE); +} + +MRN_API my_bool mroonga_highlight_html_init(UDF_INIT *init, + UDF_ARGS *args, + char *message) +{ + MRN_DBUG_ENTER_FUNCTION(); + + mrn_highlight_html_info *info = NULL; + + init->ptr = NULL; + + if (args->arg_count < 1) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_highlight_html(): wrong number of arguments: %u for 1+", + args->arg_count); + goto error; + } + + + for (unsigned int i = 0; i < args->arg_count; ++i) { + switch (args->arg_type[i]) { + case STRING_RESULT: + /* OK */ + break; + case REAL_RESULT: + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_highlight_html(): all arguments must be string: " + "<%u>=<%g>", + i, *((double *)(args->args[i]))); + goto error; + break; + case INT_RESULT: + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_highlight_html(): all arguments must be string: " + "<%u>=<%lld>", + i, *((longlong *)(args->args[i]))); + goto error; + break; + default: + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_highlight_html(): all arguments must be string: <%u>", + i); + goto error; + break; + } + } + + init->maybe_null = 0; + + info = + reinterpret_cast<mrn_highlight_html_info *>( + mrn_my_malloc(sizeof(mrn_highlight_html_info), + MYF(MY_WME | MY_ZEROFILL))); + if (!info) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_highlight_html(): failed to allocate memory"); + goto error; + } + + info->ctx = mrn_context_pool->pull(); + { + const char *current_db_path = MRN_THD_DB_PATH(current_thd); + const char *action; + if (current_db_path) { + action = "open database"; + mrn::Database *db; + int error = mrn_db_manager->open(current_db_path, &db); + if (error == 0) { + info->db = db->get(); + grn_ctx_use(info->ctx, info->db); + info->use_shared_db = true; + } + } else { + action = "create anonymous database"; + info->db = grn_db_create(info->ctx, NULL, NULL); + info->use_shared_db = false; + } + if (!info->db) { + sprintf(message, + "mroonga_highlight_html(): failed to %s: %s", + action, + info->ctx->errbuf); + goto error; + } + } + + info->query_mode.used = FALSE; + + if (args->arg_count == 2 && + args->attribute_lengths[1] == strlen("query") && + strncmp(args->attributes[1], "query", strlen("query")) == 0) { + info->query_mode.used = TRUE; + info->query_mode.table = NULL; + info->query_mode.default_column = NULL; + } + + { + bool all_keywords_are_constant = TRUE; + for (unsigned int i = 1; i < args->arg_count; ++i) { + if (!args->args[i]) { + all_keywords_are_constant = FALSE; + break; + } + } + + if (all_keywords_are_constant) { + if (mrn_highlight_html_prepare(info, args, message, &(info->keywords))) { + goto error; + } + } else { + info->keywords = NULL; + } + } + + init->ptr = (char *)info; + + DBUG_RETURN(FALSE); + +error: + if (info) { + if (!info->use_shared_db) { + grn_obj_close(info->ctx, info->db); + } + mrn_context_pool->release(info->ctx); + my_free(info); + } + DBUG_RETURN(TRUE); +} + +static bool highlight_html(grn_ctx *ctx, + grn_pat *keywords, + const char *target, + size_t target_length, + String *output) +{ + MRN_DBUG_ENTER_FUNCTION(); + + grn_obj buffer; + + GRN_TEXT_INIT(&buffer, 0); + + { + const char *open_tag = "<span class=\"keyword\">"; + size_t open_tag_length = strlen(open_tag); + const char *close_tag = "</span>"; + size_t close_tag_length = strlen(close_tag); + + while (target_length > 0) { +#define MAX_N_HITS 16 + grn_pat_scan_hit hits[MAX_N_HITS]; + const char *rest; + size_t previous = 0; + size_t chunk_length; + + int n_hits = grn_pat_scan(ctx, + keywords, + target, + target_length, + hits, MAX_N_HITS, &rest); + for (int i = 0; i < n_hits; i++) { + if ((hits[i].offset - previous) > 0) { + grn_text_escape_xml(ctx, + &buffer, + target + previous, + hits[i].offset - previous); + } + GRN_TEXT_PUT(ctx, &buffer, open_tag, open_tag_length); + grn_text_escape_xml(ctx, + &buffer, + target + hits[i].offset, + hits[i].length); + GRN_TEXT_PUT(ctx, &buffer, close_tag, close_tag_length); + previous = hits[i].offset + hits[i].length; + } + + chunk_length = rest - target; + if ((chunk_length - previous) > 0) { + grn_text_escape_xml(ctx, + &buffer, + target + previous, + target_length - previous); + } + target_length -= chunk_length; + target = rest; +#undef MAX_N_HITS + } + } + + if (output->reserve(GRN_TEXT_LEN(&buffer))) { + my_error(ER_OUT_OF_RESOURCES, MYF(0), HA_ERR_OUT_OF_MEM); + GRN_OBJ_FIN(ctx, &buffer); + DBUG_RETURN(false); + } + + output->q_append(GRN_TEXT_VALUE(&buffer), GRN_TEXT_LEN(&buffer)); + GRN_OBJ_FIN(ctx, &buffer); + DBUG_RETURN(true); +} + +MRN_API char *mroonga_highlight_html(UDF_INIT *init, + UDF_ARGS *args, + char *result, + unsigned long *length, + char *is_null, + char *error) +{ + MRN_DBUG_ENTER_FUNCTION(); + + mrn_highlight_html_info *info = + reinterpret_cast<mrn_highlight_html_info *>(init->ptr); + + grn_ctx *ctx = info->ctx; + grn_obj *keywords = info->keywords; + String *result_str = &(info->result_str); + + if (!args->args[0]) { + *is_null = 1; + DBUG_RETURN(NULL); + } + + if (!keywords) { + if (mrn_highlight_html_prepare(info, args, NULL, &keywords)) { + goto error; + } + } + + *is_null = 0; + result_str->length(0); + + if (!highlight_html(ctx, + reinterpret_cast<grn_pat *>(keywords), + args->args[0], + args->lengths[0], + result_str)) { + goto error; + } + + if (!info->keywords) { + grn_rc rc = grn_obj_close(ctx, keywords); + if (rc != GRN_SUCCESS) { + my_printf_error(ER_MRN_ERROR_FROM_GROONGA_NUM, + ER_MRN_ERROR_FROM_GROONGA_STR, MYF(0), ctx->errbuf); + goto error; + } + } + + *length = result_str->length(); + DBUG_RETURN((char *)result_str->ptr()); + +error: + if (!info->keywords && keywords) { + grn_obj_close(ctx, keywords); + } + + *is_null = 1; + *error = 1; + + DBUG_RETURN(NULL); +} + +MRN_API void mroonga_highlight_html_deinit(UDF_INIT *init) +{ + MRN_DBUG_ENTER_FUNCTION(); + + mrn_highlight_html_info *info = + reinterpret_cast<mrn_highlight_html_info *>(init->ptr); + if (!info) { + DBUG_VOID_RETURN; + } + + if (info->keywords) { + grn_obj_close(info->ctx, info->keywords); + } + if (info->query_mode.used) { + if (info->query_mode.default_column) { + grn_obj_close(info->ctx, info->query_mode.default_column); + } + if (info->query_mode.table) { + grn_obj_close(info->ctx, info->query_mode.table); + } + } + MRN_STRING_FREE(info->result_str); + if (!info->use_shared_db) { + grn_obj_close(info->ctx, info->db); + } + mrn_context_pool->release(info->ctx); + my_free(info); + + DBUG_VOID_RETURN; +} + +MRN_END_DECLS diff --git a/storage/mroonga/udf/mrn_udf_last_insert_grn_id.cpp b/storage/mroonga/udf/mrn_udf_last_insert_grn_id.cpp index b54f5c53206..fb4b5440ef1 100644 --- a/storage/mroonga/udf/mrn_udf_last_insert_grn_id.cpp +++ b/storage/mroonga/udf/mrn_udf_last_insert_grn_id.cpp @@ -2,7 +2,7 @@ /* Copyright(C) 2010 Tetsuro IKEDA Copyright(C) 2010-2013 Kentoku SHIBA - Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com> + 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 @@ -23,20 +23,21 @@ #include <mrn_windows.hpp> #include <mrn_table.hpp> #include <mrn_macro.hpp> +#include <mrn_current_thread.hpp> MRN_BEGIN_DECLS -MRN_API my_bool last_insert_grn_id_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +MRN_API my_bool last_insert_grn_id_init(UDF_INIT *init, UDF_ARGS *args, char *message) { if (args->arg_count != 0) { strcpy(message, "last_insert_grn_id must not have arguments"); return 1; } - initid->maybe_null = 0; + init->maybe_null = 0; return 0; } -MRN_API longlong last_insert_grn_id(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) +MRN_API longlong last_insert_grn_id(UDF_INIT *init, UDF_ARGS *args, char *is_null, char *error) { THD *thd = current_thd; st_mrn_slot_data *slot_data = mrn_get_slot_data(thd, false); @@ -47,7 +48,7 @@ MRN_API longlong last_insert_grn_id(UDF_INIT *initid, UDF_ARGS *args, char *is_n return last_insert_record_id; } -MRN_API void last_insert_grn_id_deinit(UDF_INIT *initid) +MRN_API void last_insert_grn_id_deinit(UDF_INIT *init) { } diff --git a/storage/mroonga/udf/mrn_udf_normalize.cpp b/storage/mroonga/udf/mrn_udf_normalize.cpp new file mode 100644 index 00000000000..dd597946ea8 --- /dev/null +++ b/storage/mroonga/udf/mrn_udf_normalize.cpp @@ -0,0 +1,212 @@ +/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */ +/* + Copyright(C) 2015 Naoya Murakami <naoya@createfield.com> + 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_mysql.h> +#include <mrn_mysql_compat.h> +#include <mrn_encoding.hpp> +#include <mrn_windows.hpp> +#include <mrn_table.hpp> +#include <mrn_macro.hpp> +#include <mrn_database_manager.hpp> +#include <mrn_context_pool.hpp> +#include <mrn_variables.hpp> +#include <mrn_current_thread.hpp> + +MRN_BEGIN_DECLS + +extern mrn::DatabaseManager *mrn_db_manager; +extern mrn::ContextPool *mrn_context_pool; + +#define DEFAULT_NORMALIZER_NAME "NormalizerAuto" + +struct st_mrn_normalize_info +{ + grn_ctx *ctx; + grn_obj *db; + bool use_shared_db; + grn_obj *normalizer; + int flags; + String result_str; +}; + +MRN_API my_bool mroonga_normalize_init(UDF_INIT *init, UDF_ARGS *args, + char *message) +{ + st_mrn_normalize_info *info = NULL; + String *result_str = NULL; + + init->ptr = NULL; + if (!(1 <= args->arg_count && args->arg_count <= 2)) { + sprintf(message, + "mroonga_normalize(): Incorrect number of arguments: %u for 1..2", + args->arg_count); + goto error; + } + if (args->arg_type[0] != STRING_RESULT) { + strcpy(message, + "mroonga_normalize(): The 1st argument must be query as string"); + goto error; + } + if (args->arg_count == 2) { + if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, + "mroonga_normalize(): " + "The 2st argument must be normalizer name as string"); + goto error; + } + } + + init->maybe_null = 1; + + info = (st_mrn_normalize_info *)mrn_my_malloc(sizeof(st_mrn_normalize_info), + MYF(MY_WME | MY_ZEROFILL)); + if (!info) { + strcpy(message, "mroonga_normalize(): out of memory"); + goto error; + } + + info->ctx = mrn_context_pool->pull(); + { + const char *current_db_path = MRN_THD_DB_PATH(current_thd); + const char *action; + if (current_db_path) { + action = "open database"; + mrn::Database *db; + int error = mrn_db_manager->open(current_db_path, &db); + if (error == 0) { + info->db = db->get(); + grn_ctx_use(info->ctx, info->db); + info->use_shared_db = true; + } + } else { + action = "create anonymous database"; + info->db = grn_db_create(info->ctx, NULL, NULL); + info->use_shared_db = false; + } + if (!info->db) { + sprintf(message, + "mroonga_normalize(): failed to %s: %s", + action, + info->ctx->errbuf); + goto error; + } + } + + if (args->arg_count == 1) { + info->normalizer = grn_ctx_get(info->ctx, DEFAULT_NORMALIZER_NAME, -1); + } else { + info->normalizer = grn_ctx_get(info->ctx, args->args[1], args->lengths[1]); + } + if (!info->normalizer) { + sprintf(message, "mroonga_normalize(): nonexistent normalizer %.*s", + (int)args->lengths[1], args->args[1]); + goto error; + } + info->flags = 0; + + result_str = &(info->result_str); + mrn::encoding::set_raw(info->ctx, system_charset_info); + result_str->set_charset(system_charset_info); + + init->ptr = (char *)info; + + return FALSE; + +error: + if (info) { + if (!info->use_shared_db) { + grn_obj_close(info->ctx, info->db); + } + mrn_context_pool->release(info->ctx); + my_free(info); + } + return TRUE; +} + +MRN_API char *mroonga_normalize(UDF_INIT *init, UDF_ARGS *args, char *result, + unsigned long *length, char *is_null, char *error) +{ + st_mrn_normalize_info *info = (st_mrn_normalize_info *)init->ptr; + grn_ctx *ctx = info->ctx; + String *result_str = &(info->result_str); + + if (!args->args[0]) { + *is_null = 1; + return NULL; + } + + result_str->length(0); + { + char *target = args->args[0]; + unsigned int target_length = args->lengths[0]; + grn_obj *grn_string; + const char *normalized; + unsigned int normalized_length_in_bytes; + unsigned int normalized_n_characters; + + grn_string = grn_string_open(ctx, + target, target_length, + info->normalizer, info->flags); + grn_string_get_normalized(ctx, grn_string, + &normalized, + &normalized_length_in_bytes, + &normalized_n_characters); + if (result_str->reserve(normalized_length_in_bytes)) { + my_error(ER_OUT_OF_RESOURCES, MYF(0), HA_ERR_OUT_OF_MEM); + goto error; + } + result_str->q_append(normalized, normalized_length_in_bytes); + result_str->length(normalized_length_in_bytes); + grn_obj_unlink(ctx, grn_string); + } + *is_null = 0; + + if (ctx->rc) { + my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0)); + goto error; + } + + *length = result_str->length(); + return (char *)result_str->ptr(); + +error: + *is_null = 1; + *error = 1; + return NULL; +} + +MRN_API void mroonga_normalize_deinit(UDF_INIT *init) +{ + st_mrn_normalize_info *info = (st_mrn_normalize_info *)init->ptr; + + if (info) { + MRN_STRING_FREE(info->result_str); + if (info->normalizer) { + grn_obj_unlink(info->ctx, info->normalizer); + } + if (!info->use_shared_db) { + grn_obj_close(info->ctx, info->db); + } + mrn_context_pool->release(info->ctx); + my_free(info); + } +} + +MRN_END_DECLS diff --git a/storage/mroonga/udf/mrn_udf_query_expand.cpp b/storage/mroonga/udf/mrn_udf_query_expand.cpp new file mode 100644 index 00000000000..562499242fc --- /dev/null +++ b/storage/mroonga/udf/mrn_udf_query_expand.cpp @@ -0,0 +1,282 @@ +/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */ +/* + 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_mysql.h> +#include <mrn_mysql_compat.h> +#include <mrn_path_mapper.hpp> +#include <mrn_windows.hpp> +#include <mrn_macro.hpp> +#include <mrn_variables.hpp> +#include <mrn_database_manager.hpp> +#include <mrn_context_pool.hpp> +#include <mrn_current_thread.hpp> +#include <mrn_query_parser.hpp> + +MRN_BEGIN_DECLS + +extern mrn::DatabaseManager *mrn_db_manager; +extern mrn::ContextPool *mrn_context_pool; + +namespace mrn { + struct QueryExpandInfo { + grn_ctx *ctx; + grn_obj expanded_query; + grn_obj *term_column; + grn_obj *expanded_term_column; + }; +} + +static void mrn_query_expand_info_free(mrn::QueryExpandInfo *info) +{ + MRN_DBUG_ENTER_FUNCTION(); + + if (!info) { + DBUG_VOID_RETURN; + } + + if (info->ctx) { + GRN_OBJ_FIN(info->ctx, &(info->expanded_query)); + if (grn_obj_is_accessor(info->ctx, info->expanded_term_column)) { + grn_obj_unlink(info->ctx, info->expanded_term_column); + } + if (grn_obj_is_accessor(info->ctx, info->term_column)) { + grn_obj_unlink(info->ctx, info->term_column); + } + mrn_context_pool->release(info->ctx); + } + my_free(info); + + DBUG_VOID_RETURN; +} + +MRN_API my_bool mroonga_query_expand_init(UDF_INIT *init, + UDF_ARGS *args, + char *message) +{ + mrn::QueryExpandInfo *info = NULL; + + MRN_DBUG_ENTER_FUNCTION(); + + init->ptr = NULL; + if (args->arg_count != 4) { + sprintf(message, + "mroonga_query_expand(): wrong number of arguments: %u for 4", + args->arg_count); + goto error; + } + if (args->arg_type[0] != STRING_RESULT) { + strcpy(message, + "mroonga_query_expand(): " + "the 1st argument must be table name as string"); + goto error; + } + if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, + "mroonga_query_expand(): " + "the 2nd argument must be term column name as string"); + goto error; + } + if (args->arg_type[2] != STRING_RESULT) { + strcpy(message, + "mroonga_query_expand(): " + "the 3nd argument must be expanded term column name as string"); + goto error; + } + if (args->arg_type[3] != STRING_RESULT) { + strcpy(message, + "mroonga_query_expand(): " + "the 4th argument must be query as string"); + goto error; + } + + init->maybe_null = 1; + + info = static_cast<mrn::QueryExpandInfo *>( + mrn_my_malloc(sizeof(mrn::QueryExpandInfo), + MYF(MY_WME | MY_ZEROFILL))); + if (!info) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_query_expand(): failed to allocate memory"); + goto error; + } + + { + const char *current_db_path = MRN_THD_DB_PATH(current_thd); + if (!current_db_path) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_query_expand(): no current database"); + goto error; + } + + mrn::Database *db; + int error = mrn_db_manager->open(current_db_path, &db); + if (error != 0) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_query_expand(): failed to open database: %s", + mrn_db_manager->error_message()); + goto error; + } + info->ctx = mrn_context_pool->pull(); + grn_ctx_use(info->ctx, db->get()); + } + + GRN_TEXT_INIT(&(info->expanded_query), 0); + + { + const char *table_name = args->args[0]; + unsigned int table_name_length = args->lengths[0]; + grn_obj *table = grn_ctx_get(info->ctx, + table_name, + table_name_length); + if (!table) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_query_expand(): table doesn't exist: <%.*s>", + static_cast<int>(table_name_length), + table_name); + goto error; + } + + const char *term_column_name = args->args[1]; + unsigned int term_column_name_length = args->lengths[1]; + info->term_column = grn_obj_column(info->ctx, + table, + term_column_name, + term_column_name_length); + if (!info->term_column) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_query_expand(): term column doesn't exist: <%.*s.%.*s>", + static_cast<int>(table_name_length), + table_name, + static_cast<int>(term_column_name_length), + term_column_name); + goto error; + } + + const char *expanded_term_column_name = args->args[2]; + unsigned int expanded_term_column_name_length = args->lengths[2]; + info->expanded_term_column = grn_obj_column(info->ctx, + table, + expanded_term_column_name, + expanded_term_column_name_length); + if (!info->expanded_term_column) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_query_expand(): " + "expanded term column doesn't exist: <%.*s.%.*s>", + static_cast<int>(table_name_length), + table_name, + static_cast<int>(expanded_term_column_name_length), + expanded_term_column_name); + goto error; + } + } + + init->ptr = reinterpret_cast<char *>(info); + + DBUG_RETURN(FALSE); + +error: + mrn_query_expand_info_free(info); + DBUG_RETURN(TRUE); +} + +static void query_expand(mrn::QueryExpandInfo *info, UDF_ARGS *args) +{ + grn_ctx *ctx = info->ctx; + const char *query = args->args[3]; + unsigned int query_length = args->lengths[3]; + + mrn::QueryParser query_parser(info->ctx, + current_thd, + NULL, + NULL, + 0, + NULL); + const char *raw_query; + size_t raw_query_length; + grn_operator default_operator; + grn_expr_flags flags; + query_parser.parse_pragma(query, + query_length, + &raw_query, + &raw_query_length, + &default_operator, + &flags); + GRN_TEXT_SET(info->ctx, + &(info->expanded_query), + query, + raw_query - query); + grn_expr_syntax_expand_query_by_table(ctx, + raw_query, + raw_query_length, + flags, + info->term_column, + info->expanded_term_column, + &(info->expanded_query)); +} + +MRN_API char *mroonga_query_expand(UDF_INIT *init, + UDF_ARGS *args, + char *result, + unsigned long *length, + char *is_null, + char *error) +{ + MRN_DBUG_ENTER_FUNCTION(); + + mrn::QueryExpandInfo *info = + reinterpret_cast<mrn::QueryExpandInfo *>(init->ptr); + grn_ctx *ctx = info->ctx; + + if (!args->args[3]) { + *is_null = 1; + DBUG_RETURN(NULL); + } + + *is_null = 0; + + query_expand(info, args); + + if (ctx->rc) { + char message[MYSQL_ERRMSG_SIZE]; + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_query_expand(): " + "failed to expand: %s", + ctx->errbuf); + my_message(ER_ERROR_ON_WRITE, message, MYF(0)); + goto error; + } + + *length = GRN_TEXT_LEN(&(info->expanded_query)); + DBUG_RETURN(GRN_TEXT_VALUE(&(info->expanded_query))); + +error: + *error = 1; + DBUG_RETURN(NULL); +} + +MRN_API void mroonga_query_expand_deinit(UDF_INIT *init) +{ + MRN_DBUG_ENTER_FUNCTION(); + mrn::QueryExpandInfo *info = + reinterpret_cast<mrn::QueryExpandInfo *>(init->ptr); + mrn_query_expand_info_free(info); + DBUG_VOID_RETURN; +} + +MRN_END_DECLS diff --git a/storage/mroonga/udf/mrn_udf_snippet.cpp b/storage/mroonga/udf/mrn_udf_snippet.cpp index 22ec0884014..7a35225545d 100644 --- a/storage/mroonga/udf/mrn_udf_snippet.cpp +++ b/storage/mroonga/udf/mrn_udf_snippet.cpp @@ -2,7 +2,7 @@ /* Copyright(C) 2010 Tetsuro IKEDA Copyright(C) 2010-2013 Kentoku SHIBA - Copyright(C) 2011-2014 Kouhei Sutou <kou@clear-code.com> + 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 @@ -26,13 +26,21 @@ #include <mrn_windows.hpp> #include <mrn_table.hpp> #include <mrn_macro.hpp> +#include <mrn_database_manager.hpp> +#include <mrn_context_pool.hpp> #include <mrn_variables.hpp> +#include <mrn_current_thread.hpp> MRN_BEGIN_DECLS +extern mrn::DatabaseManager *mrn_db_manager; +extern mrn::ContextPool *mrn_context_pool; + struct st_mrn_snip_info { - grn_ctx ctx; + grn_ctx *ctx; + grn_obj *db; + bool use_shared_db; grn_obj *snippet; String result_str; }; @@ -42,7 +50,7 @@ static my_bool mrn_snippet_prepare(st_mrn_snip_info *snip_info, UDF_ARGS *args, { unsigned int i; CHARSET_INFO *cs; - grn_ctx *ctx = &snip_info->ctx; + grn_ctx *ctx = snip_info->ctx; long long snip_max_len; long long snip_max_num; long long skip_leading_spaces; @@ -121,12 +129,12 @@ error: return TRUE; } -MRN_API my_bool mroonga_snippet_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +MRN_API my_bool mroonga_snippet_init(UDF_INIT *init, UDF_ARGS *args, char *message) { uint i; st_mrn_snip_info *snip_info = NULL; bool can_open_snippet = TRUE; - initid->ptr = NULL; + init->ptr = NULL; if (args->arg_count < 11 || (args->arg_count - 11) % 3) { sprintf(message, "Incorrect number of arguments for mroonga_snippet(): %u", @@ -168,8 +176,7 @@ MRN_API my_bool mroonga_snippet_init(UDF_INIT *initid, UDF_ARGS *args, char *mes goto error; } } - initid->maybe_null = 1; - initid->const_item = 1; + init->maybe_null = 1; if (!(snip_info = (st_mrn_snip_info *) mrn_my_malloc(sizeof(st_mrn_snip_info), MYF(MY_WME | MY_ZEROFILL)))) @@ -177,8 +184,32 @@ MRN_API my_bool mroonga_snippet_init(UDF_INIT *initid, UDF_ARGS *args, char *mes strcpy(message, "mroonga_snippet() out of memory"); goto error; } - grn_ctx_init(&snip_info->ctx, 0); - grn_db_create(&snip_info->ctx, NULL, 0); + snip_info->ctx = mrn_context_pool->pull(); + { + const char *current_db_path = MRN_THD_DB_PATH(current_thd); + const char *action; + if (current_db_path) { + action = "open database"; + mrn::Database *db; + int error = mrn_db_manager->open(current_db_path, &db); + if (error == 0) { + snip_info->db = db->get(); + grn_ctx_use(snip_info->ctx, snip_info->db); + snip_info->use_shared_db = true; + } + } else { + action = "create anonymous database"; + snip_info->db = grn_db_create(snip_info->ctx, NULL, NULL); + snip_info->use_shared_db = false; + } + if (!snip_info->db) { + sprintf(message, + "mroonga_snippet(): failed to %s: %s", + action, + snip_info->ctx->errbuf); + goto error; + } + } for (i = 1; i < args->arg_count; i++) { if (!args->args[i]) { @@ -191,24 +222,26 @@ MRN_API my_bool mroonga_snippet_init(UDF_INIT *initid, UDF_ARGS *args, char *mes goto error; } } - initid->ptr = (char *) snip_info; + init->ptr = (char *) snip_info; return FALSE; error: if (snip_info) { - grn_obj_close(&snip_info->ctx, grn_ctx_db(&snip_info->ctx)); - grn_ctx_fin(&snip_info->ctx); + if (!snip_info->use_shared_db) { + grn_obj_close(snip_info->ctx, snip_info->db); + } + mrn_context_pool->release(snip_info->ctx); my_free(snip_info); } return TRUE; } -MRN_API char *mroonga_snippet(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *length, char *is_null, char *error) +MRN_API char *mroonga_snippet(UDF_INIT *init, UDF_ARGS *args, char *result, + unsigned long *length, char *is_null, char *error) { - st_mrn_snip_info *snip_info = (st_mrn_snip_info *) initid->ptr; - grn_ctx *ctx = &snip_info->ctx; + st_mrn_snip_info *snip_info = (st_mrn_snip_info *) init->ptr; + grn_ctx *ctx = snip_info->ctx; String *result_str = &snip_info->result_str; char *target; unsigned int target_length; @@ -286,16 +319,18 @@ error: return NULL; } -MRN_API void mroonga_snippet_deinit(UDF_INIT *initid) +MRN_API void mroonga_snippet_deinit(UDF_INIT *init) { - st_mrn_snip_info *snip_info = (st_mrn_snip_info *) initid->ptr; + st_mrn_snip_info *snip_info = (st_mrn_snip_info *) init->ptr; if (snip_info) { if (snip_info->snippet) { - grn_obj_close(&snip_info->ctx, snip_info->snippet); + grn_obj_close(snip_info->ctx, snip_info->snippet); } MRN_STRING_FREE(snip_info->result_str); - grn_obj_close(&snip_info->ctx, grn_ctx_db(&snip_info->ctx)); - grn_ctx_fin(&snip_info->ctx); + if (!snip_info->use_shared_db) { + grn_obj_close(snip_info->ctx, snip_info->db); + } + mrn_context_pool->release(snip_info->ctx); my_free(snip_info); } } diff --git a/storage/mroonga/udf/mrn_udf_snippet_html.cpp b/storage/mroonga/udf/mrn_udf_snippet_html.cpp new file mode 100644 index 00000000000..99c9edfbba8 --- /dev/null +++ b/storage/mroonga/udf/mrn_udf_snippet_html.cpp @@ -0,0 +1,444 @@ +/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */ +/* + 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 + 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_err.h> +#include <mrn_encoding.hpp> +#include <mrn_windows.hpp> +#include <mrn_table.hpp> +#include <mrn_macro.hpp> +#include <mrn_database_manager.hpp> +#include <mrn_context_pool.hpp> +#include <mrn_variables.hpp> +#include <mrn_query_parser.hpp> +#include <mrn_current_thread.hpp> + +MRN_BEGIN_DECLS + +extern mrn::DatabaseManager *mrn_db_manager; +extern mrn::ContextPool *mrn_context_pool; + +typedef struct st_mrn_snippet_html_info +{ + grn_ctx *ctx; + grn_obj *db; + bool use_shared_db; + grn_obj *snippet; + String result_str; + struct { + bool used; + grn_obj *table; + grn_obj *default_column; + } query_mode; +} mrn_snippet_html_info; + +static my_bool mrn_snippet_html_prepare(mrn_snippet_html_info *info, + UDF_ARGS *args, + char *message, + grn_obj **snippet) +{ + MRN_DBUG_ENTER_FUNCTION(); + + grn_ctx *ctx = info->ctx; + int flags = GRN_SNIP_SKIP_LEADING_SPACES; + unsigned int width = 200; + unsigned int max_n_results = 3; + const char *open_tag = "<span class=\"keyword\">"; + const char *close_tag = "</span>"; + grn_snip_mapping *mapping = GRN_SNIP_MAPPING_HTML_ESCAPE; + grn_obj *expr = NULL; + String *result_str = &(info->result_str); + + *snippet = NULL; + + mrn::encoding::set_raw(ctx, system_charset_info); + if (!(system_charset_info->state & (MY_CS_BINSORT | MY_CS_CSSORT))) { + flags |= GRN_SNIP_NORMALIZE; + } + + *snippet = grn_snip_open(ctx, flags, + width, max_n_results, + open_tag, strlen(open_tag), + close_tag, strlen(close_tag), + mapping); + if (ctx->rc != GRN_SUCCESS) { + if (message) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_snippet_html(): failed to open grn_snip: <%s>", + ctx->errbuf); + } + goto error; + } + + if (info->query_mode.used) { + if (!info->query_mode.table) { + grn_obj *short_text; + short_text = grn_ctx_at(info->ctx, GRN_DB_SHORT_TEXT); + info->query_mode.table = grn_table_create(info->ctx, + NULL, 0, NULL, + GRN_TABLE_HASH_KEY, + short_text, + NULL); + } + if (!info->query_mode.default_column) { + info->query_mode.default_column = + grn_obj_column(info->ctx, + info->query_mode.table, + GRN_COLUMN_NAME_KEY, + GRN_COLUMN_NAME_KEY_LEN); + } + + grn_obj *record = NULL; + GRN_EXPR_CREATE_FOR_QUERY(info->ctx, info->query_mode.table, expr, record); + if (!expr) { + if (message) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_snippet_html(): " + "failed to create expression: <%s>", + ctx->errbuf); + } + goto error; + } + + mrn::QueryParser query_parser(info->ctx, + current_thd, + expr, + info->query_mode.default_column, + 0, + NULL); + grn_rc rc = query_parser.parse(args->args[1], args->lengths[1]); + if (rc != GRN_SUCCESS) { + if (message) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_snippet_html(): " + "failed to parse query: <%s>", + ctx->errbuf); + } + goto error; + } + + rc = grn_expr_snip_add_conditions(info->ctx, + expr, + *snippet, + 0, + NULL, NULL, + NULL, NULL); + if (rc != GRN_SUCCESS) { + if (message) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_snippet_html(): " + "failed to add conditions: <%s>", + ctx->errbuf); + } + goto error; + } + } else { + unsigned int i; + for (i = 1; i < args->arg_count; ++i) { + if (!args->args[i]) { + continue; + } + grn_rc rc = grn_snip_add_cond(ctx, *snippet, + args->args[i], args->lengths[i], + NULL, 0, + NULL, 0); + if (rc != GRN_SUCCESS) { + if (message) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_snippet_html(): " + "failed to add a condition to grn_snip: <%s>", + ctx->errbuf); + } + goto error; + } + } + } + + result_str->set_charset(system_charset_info); + DBUG_RETURN(FALSE); + +error: + if (expr) { + grn_obj_close(ctx, expr); + } + if (*snippet) { + grn_obj_close(ctx, *snippet); + } + DBUG_RETURN(TRUE); +} + +MRN_API my_bool mroonga_snippet_html_init(UDF_INIT *init, + UDF_ARGS *args, + char *message) +{ + MRN_DBUG_ENTER_FUNCTION(); + + mrn_snippet_html_info *info = NULL; + + init->ptr = NULL; + + if (args->arg_count < 1) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_snippet_html(): wrong number of arguments: %u for 1+", + args->arg_count); + goto error; + } + + + for (unsigned int i = 0; i < args->arg_count; ++i) { + switch (args->arg_type[i]) { + case STRING_RESULT: + /* OK */ + break; + case REAL_RESULT: + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_snippet_html(): all arguments must be string: " + "<%u>=<%g>", + i, *((double *)(args->args[i]))); + goto error; + break; + case INT_RESULT: + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_snippet_html(): all arguments must be string: " + "<%u>=<%lld>", + i, *((longlong *)(args->args[i]))); + goto error; + break; + default: + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_snippet_html(): all arguments must be string: <%u>", + i); + goto error; + break; + } + } + + init->maybe_null = 1; + + info = (mrn_snippet_html_info *)mrn_my_malloc(sizeof(mrn_snippet_html_info), + MYF(MY_WME | MY_ZEROFILL)); + if (!info) { + snprintf(message, MYSQL_ERRMSG_SIZE, + "mroonga_snippet_html(): failed to allocate memory"); + goto error; + } + + info->ctx = mrn_context_pool->pull(); + { + const char *current_db_path = MRN_THD_DB_PATH(current_thd); + const char *action; + if (current_db_path) { + action = "open database"; + mrn::Database *db; + int error = mrn_db_manager->open(current_db_path, &db); + if (error == 0) { + info->db = db->get(); + grn_ctx_use(info->ctx, info->db); + info->use_shared_db = true; + } + } else { + action = "create anonymous database"; + info->db = grn_db_create(info->ctx, NULL, NULL); + info->use_shared_db = false; + } + if (!info->db) { + sprintf(message, + "mroonga_snippet_html(): failed to %s: %s", + action, + info->ctx->errbuf); + goto error; + } + } + + info->query_mode.used = FALSE; + + if (args->arg_count == 2 && + args->attribute_lengths[1] == strlen("query") && + strncmp(args->attributes[1], "query", strlen("query")) == 0) { + info->query_mode.used = TRUE; + info->query_mode.table = NULL; + info->query_mode.default_column = NULL; + } + + { + bool all_keywords_are_constant = TRUE; + for (unsigned int i = 1; i < args->arg_count; ++i) { + if (!args->args[i]) { + all_keywords_are_constant = FALSE; + break; + } + } + + if (all_keywords_are_constant) { + if (mrn_snippet_html_prepare(info, args, message, &(info->snippet))) { + goto error; + } + } else { + info->snippet = NULL; + } + } + + init->ptr = (char *)info; + + DBUG_RETURN(FALSE); + +error: + if (info) { + if (!info->use_shared_db) { + grn_obj_close(info->ctx, info->db); + } + mrn_context_pool->release(info->ctx); + my_free(info); + } + DBUG_RETURN(TRUE); +} + +MRN_API char *mroonga_snippet_html(UDF_INIT *init, + UDF_ARGS *args, + char *result, + unsigned long *length, + char *is_null, + char *error) +{ + MRN_DBUG_ENTER_FUNCTION(); + + mrn_snippet_html_info *info = + reinterpret_cast<mrn_snippet_html_info *>(init->ptr); + + grn_ctx *ctx = info->ctx; + grn_obj *snippet = info->snippet; + String *result_str = &(info->result_str); + + if (!args->args[0]) { + *is_null = 1; + DBUG_RETURN(NULL); + } + + if (!snippet) { + if (mrn_snippet_html_prepare(info, args, NULL, &snippet)) { + goto error; + } + } + + { + char *target = args->args[0]; + unsigned int target_length = args->lengths[0]; + + unsigned int n_results, max_tagged_length; + { + grn_rc rc = grn_snip_exec(ctx, snippet, target, target_length, + &n_results, &max_tagged_length); + if (rc != GRN_SUCCESS) { + my_printf_error(ER_MRN_ERROR_FROM_GROONGA_NUM, + ER_MRN_ERROR_FROM_GROONGA_STR, MYF(0), ctx->errbuf); + goto error; + } + } + + *is_null = 0; + result_str->length(0); + + { + const char *start_tag = "<div class=\"snippet\">"; + const char *end_tag = "</div>"; + size_t start_tag_length = strlen(start_tag); + size_t end_tag_length = strlen(end_tag); + unsigned int max_length_per_snippet = + start_tag_length + end_tag_length + max_tagged_length; + if (result_str->reserve(max_length_per_snippet * n_results)) { + my_error(ER_OUT_OF_RESOURCES, MYF(0), HA_ERR_OUT_OF_MEM); + goto error; + } + + for (unsigned int i = 0; i < n_results; ++i) { + result_str->q_append(start_tag, start_tag_length); + + unsigned int result_length; + grn_rc rc = + grn_snip_get_result(ctx, snippet, i, + (char *)result_str->ptr() + result_str->length(), + &result_length); + if (rc) { + my_printf_error(ER_MRN_ERROR_FROM_GROONGA_NUM, + ER_MRN_ERROR_FROM_GROONGA_STR, MYF(0), ctx->errbuf); + goto error; + } + result_str->length(result_str->length() + result_length); + + result_str->q_append(end_tag, end_tag_length); + } + } + + if (!info->snippet) { + grn_rc rc = grn_obj_close(ctx, snippet); + if (rc != GRN_SUCCESS) { + my_printf_error(ER_MRN_ERROR_FROM_GROONGA_NUM, + ER_MRN_ERROR_FROM_GROONGA_STR, MYF(0), ctx->errbuf); + goto error; + } + } + } + + *length = result_str->length(); + DBUG_RETURN((char *)result_str->ptr()); + +error: + if (!info->snippet && snippet) { + grn_obj_close(ctx, snippet); + } + + *is_null = 1; + *error = 1; + + DBUG_RETURN(NULL); +} + +MRN_API void mroonga_snippet_html_deinit(UDF_INIT *init) +{ + MRN_DBUG_ENTER_FUNCTION(); + + mrn_snippet_html_info *info = + reinterpret_cast<mrn_snippet_html_info *>(init->ptr); + if (!info) { + DBUG_VOID_RETURN; + } + + if (info->snippet) { + grn_obj_close(info->ctx, info->snippet); + } + if (info->query_mode.used) { + if (info->query_mode.default_column) { + grn_obj_close(info->ctx, info->query_mode.default_column); + } + if (info->query_mode.table) { + grn_obj_close(info->ctx, info->query_mode.table); + } + } + MRN_STRING_FREE(info->result_str); + if (!info->use_shared_db) { + grn_obj_close(info->ctx, info->db); + } + mrn_context_pool->release(info->ctx); + my_free(info); + + DBUG_VOID_RETURN; +} + +MRN_END_DECLS diff --git a/storage/mroonga/udf/sources.am b/storage/mroonga/udf/sources.am index 380ab4b60cf..ac2f098e310 100644 --- a/storage/mroonga/udf/sources.am +++ b/storage/mroonga/udf/sources.am @@ -1,5 +1,9 @@ libmrn_udf_la_SOURCES = \ mrn_udf_last_insert_grn_id.cpp \ mrn_udf_snippet.cpp \ + mrn_udf_snippet_html.cpp \ mrn_udf_command.cpp \ - mrn_udf_escape.cpp + mrn_udf_escape.cpp \ + mrn_udf_normalize.cpp \ + mrn_udf_highlight_html.cpp \ + mrn_udf_query_expand.cpp |