mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-03 20:36:16 +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);
 | 
						|
  }
 | 
						|
}
 |