mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 02:46:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			364 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			364 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- c-basic-offset: 2 -*- */
 | |
| /*
 | |
|   Copyright(C) 2010 Tetsuro IKEDA
 | |
|   Copyright(C) 2010-2013 Kentoku SHIBA
 | |
|   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
 | |
|   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-1335  USA
 | |
| */
 | |
| 
 | |
| #include <mrn_mysql.h>
 | |
| 
 | |
| #include "mrn_database_manager.hpp"
 | |
| #include "mrn_encoding.hpp"
 | |
| #include "mrn_lock.hpp"
 | |
| #include "mrn_path_mapper.hpp"
 | |
| 
 | |
| #include <groonga/plugin.h>
 | |
| 
 | |
| // for debug
 | |
| #define MRN_CLASS_NAME "mrn::DatabaseManager"
 | |
| 
 | |
| #ifdef WIN32
 | |
| #  include <direct.h>
 | |
| #  define MRN_MKDIR(pathname, mode) _mkdir((pathname))
 | |
| #else
 | |
| #  include <dirent.h>
 | |
| #  include <unistd.h>
 | |
| #  define MRN_MKDIR(pathname, mode) mkdir((pathname), (mode))
 | |
| #endif
 | |
| 
 | |
| extern "C" {
 | |
|   grn_rc GRN_PLUGIN_IMPL_NAME_TAGGED(init, normalizers_mysql)(grn_ctx *ctx);
 | |
|   grn_rc GRN_PLUGIN_IMPL_NAME_TAGGED(register, normalizers_mysql)(grn_ctx *ctx);
 | |
| }
 | |
| 
 | |
| namespace mrn {
 | |
|   DatabaseManager::DatabaseManager(grn_ctx *ctx, mysql_mutex_t *mutex)
 | |
|     : ctx_(ctx),
 | |
|       cache_(NULL),
 | |
|       mutex_(mutex) {
 | |
|   }
 | |
| 
 | |
|   DatabaseManager::~DatabaseManager(void) {
 | |
|     if (cache_) {
 | |
|       void *db_address;
 | |
|       GRN_HASH_EACH(ctx_, cache_, id, NULL, 0, &db_address, {
 | |
|         Database *db;
 | |
|         memcpy(&db, db_address, sizeof(grn_obj *));
 | |
|         delete db;
 | |
|       });
 | |
|       grn_hash_close(ctx_, cache_);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool DatabaseManager::init(void) {
 | |
|     MRN_DBUG_ENTER_METHOD();
 | |
|     cache_ = grn_hash_create(ctx_,
 | |
|                              NULL,
 | |
|                              GRN_TABLE_MAX_KEY_SIZE,
 | |
|                              sizeof(grn_obj *),
 | |
|                              GRN_OBJ_KEY_VAR_SIZE);
 | |
|     if (!cache_) {
 | |
|       GRN_LOG(ctx_, GRN_LOG_ERROR,
 | |
|               "failed to initialize hash table for caching opened databases");
 | |
|       DBUG_RETURN(false);
 | |
|     }
 | |
| 
 | |
|     DBUG_RETURN(true);
 | |
|   }
 | |
| 
 | |
|   int DatabaseManager::open(const char *path, Database **db) {
 | |
|     MRN_DBUG_ENTER_METHOD();
 | |
| 
 | |
|     int error = 0;
 | |
|     *db = NULL;
 | |
| 
 | |
|     mrn::PathMapper mapper(path);
 | |
|     mrn::Lock lock(mutex_);
 | |
| 
 | |
|     error = mrn::encoding::set(ctx_, system_charset_info);
 | |
|     if (error) {
 | |
|       DBUG_RETURN(error);
 | |
|     }
 | |
| 
 | |
|     grn_id id;
 | |
|     void *db_address;
 | |
|     id = grn_hash_get(ctx_, cache_,
 | |
|                       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 &&
 | |
|             mrn_is_directory_separator(path[1])) {
 | |
|           ensure_database_directory();
 | |
|         }
 | |
|         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 {
 | |
|         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(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(Database *));
 | |
|       grn_ctx_use(ctx_, (*db)->get());
 | |
|     }
 | |
| 
 | |
|     DBUG_RETURN(error);
 | |
|   }
 | |
| 
 | |
|   void DatabaseManager::close(const char *path) {
 | |
|     MRN_DBUG_ENTER_METHOD();
 | |
| 
 | |
|     mrn::PathMapper mapper(path);
 | |
|     mrn::Lock lock(mutex_);
 | |
| 
 | |
|     grn_id id;
 | |
|     void *db_address;
 | |
|     id = grn_hash_get(ctx_, cache_,
 | |
|                       mapper.db_name(), strlen(mapper.db_name()),
 | |
|                       &db_address);
 | |
|     if (id == GRN_ID_NIL) {
 | |
|       DBUG_VOID_RETURN;
 | |
|     }
 | |
| 
 | |
|     Database *db = NULL;
 | |
|     memcpy(&db, db_address, sizeof(Database *));
 | |
|     grn_ctx_use(ctx_, db->get());
 | |
|     if (db) {
 | |
|       delete db;
 | |
|     }
 | |
| 
 | |
|     grn_hash_delete_by_id(ctx_, cache_, id, NULL);
 | |
| 
 | |
|     DBUG_VOID_RETURN;
 | |
|   }
 | |
| 
 | |
|   bool DatabaseManager::drop(const char *path) {
 | |
|     MRN_DBUG_ENTER_METHOD();
 | |
| 
 | |
|     mrn::PathMapper mapper(path);
 | |
|     mrn::Lock lock(mutex_);
 | |
| 
 | |
|     grn_id id;
 | |
|     void *db_address;
 | |
|     id = grn_hash_get(ctx_, cache_,
 | |
|                       mapper.db_name(), strlen(mapper.db_name()),
 | |
|                       &db_address);
 | |
| 
 | |
|     Database *db = NULL;
 | |
|     if (id == GRN_ID_NIL) {
 | |
|       struct stat dummy;
 | |
|       if (stat(mapper.db_path(), &dummy) == 0) {
 | |
|         grn_obj *grn_db = grn_db_open(ctx_, mapper.db_path());
 | |
|         db = new Database(ctx_, grn_db);
 | |
|       }
 | |
|     } else {
 | |
|       memcpy(&db, db_address, sizeof(Database *));
 | |
|       grn_ctx_use(ctx_, db->get());
 | |
|     }
 | |
| 
 | |
|     if (!db) {
 | |
|       DBUG_RETURN(false);
 | |
|     }
 | |
| 
 | |
|     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);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   int DatabaseManager::clear(void) {
 | |
|     MRN_DBUG_ENTER_METHOD();
 | |
| 
 | |
|     int error = 0;
 | |
| 
 | |
|     mrn::Lock lock(mutex_);
 | |
| 
 | |
|     grn_hash_cursor *cursor;
 | |
|     cursor = grn_hash_cursor_open(ctx_, cache_,
 | |
|                                   NULL, 0, NULL, 0,
 | |
|                                   0, -1, 0);
 | |
|     if (ctx_->rc) {
 | |
|       my_message(ER_ERROR_ON_READ, ctx_->errbuf, MYF(0));
 | |
|       DBUG_RETURN(ER_ERROR_ON_READ);
 | |
|     }
 | |
| 
 | |
|     while (grn_hash_cursor_next(ctx_, cursor) != GRN_ID_NIL) {
 | |
|       if (ctx_->rc) {
 | |
|         error = ER_ERROR_ON_READ;
 | |
|         my_message(error, ctx_->errbuf, MYF(0));
 | |
|         break;
 | |
|       }
 | |
|       void *db_address;
 | |
|       Database *db;
 | |
|       grn_hash_cursor_get_value(ctx_, cursor, &db_address);
 | |
|       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;
 | |
|       }
 | |
|       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();
 | |
| 
 | |
|     int i = 0;
 | |
|     char sub_directory[MRN_MAX_PATH_SIZE];
 | |
|     sub_directory[0] = '\0';
 | |
|     while (true) {
 | |
|       if (mrn_is_directory_separator(directory[i]) ||
 | |
|           directory[i] == '\0') {
 | |
|         sub_directory[i] = '\0';
 | |
|         struct stat directory_status;
 | |
|         if (stat(sub_directory, &directory_status) != 0) {
 | |
|           DBUG_PRINT("info", ("mroonga: creating directory: <%s>", sub_directory));
 | |
|           GRN_LOG(ctx_, GRN_LOG_INFO, "creating directory: <%s>", sub_directory);
 | |
|           if (MRN_MKDIR(sub_directory, S_IRWXU) == 0) {
 | |
|             DBUG_PRINT("info",
 | |
|                        ("mroonga: created directory: <%s>", sub_directory));
 | |
|             GRN_LOG(ctx_, GRN_LOG_INFO, "created directory: <%s>", sub_directory);
 | |
|           } else {
 | |
|             DBUG_PRINT("error",
 | |
|                        ("mroonga: failed to create directory: <%s>: <%s>",
 | |
|                         sub_directory, strerror(errno)));
 | |
|             GRN_LOG(ctx_, GRN_LOG_ERROR,
 | |
|                     "failed to create directory: <%s>: <%s>",
 | |
|                     sub_directory, strerror(errno));
 | |
|             DBUG_VOID_RETURN;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (directory[i] == '\0') {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       sub_directory[i] = directory[i];
 | |
|       ++i;
 | |
|     }
 | |
| 
 | |
|     DBUG_VOID_RETURN;
 | |
|   }
 | |
| 
 | |
|   void DatabaseManager::ensure_database_directory(void) {
 | |
|     MRN_DBUG_ENTER_METHOD();
 | |
| 
 | |
|     const char *path_prefix = mrn::PathMapper::default_path_prefix;
 | |
|     if (!path_prefix)
 | |
|       DBUG_VOID_RETURN;
 | |
| 
 | |
|     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)
 | |
|       DBUG_VOID_RETURN;
 | |
| 
 | |
|     char database_directory[MRN_MAX_PATH_SIZE];
 | |
|     size_t database_directory_length = last_path_separator - path_prefix;
 | |
|     strncpy(database_directory, path_prefix, database_directory_length);
 | |
|     database_directory[database_directory_length] = '\0';
 | |
|     mkdir_p(database_directory);
 | |
| 
 | |
|     DBUG_VOID_RETURN;
 | |
|   }
 | |
| 
 | |
|   int DatabaseManager::ensure_normalizers_registered(grn_obj *db) {
 | |
|     MRN_DBUG_ENTER_METHOD();
 | |
| 
 | |
|     int error = 0;
 | |
| #ifdef WITH_GROONGA_NORMALIZER_MYSQL
 | |
|     {
 | |
| #  ifdef MRN_GROONGA_NORMALIZER_MYSQL_EMBEDDED
 | |
|       GRN_PLUGIN_IMPL_NAME_TAGGED(init, normalizers_mysql)(ctx_);
 | |
|       GRN_PLUGIN_IMPL_NAME_TAGGED(register, normalizers_mysql)(ctx_);
 | |
| #  else
 | |
|       grn_obj *mysql_normalizer;
 | |
|       mysql_normalizer = grn_ctx_get(ctx_, "NormalizerMySQLGeneralCI", -1);
 | |
|       if (mysql_normalizer) {
 | |
|         grn_obj_unlink(ctx_, mysql_normalizer);
 | |
|       } else {
 | |
|         grn_plugin_register(ctx_, GROONGA_NORMALIZER_MYSQL_PLUGIN_NAME);
 | |
|       }
 | |
| #  endif
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     DBUG_RETURN(error);
 | |
|   }
 | |
| }
 | 
