1 /* -*- c-basic-offset: 2 -*- */ 2 /* 3 Copyright(C) 2010 Tetsuro IKEDA 4 Copyright(C) 2010-2013 Kentoku SHIBA 5 Copyright(C) 2011-2015 Kouhei Sutou <kou@clear-code.com> 6 7 This library is free software; you can redistribute it and/or 8 modify it under the terms of the GNU Lesser General Public 9 License as published by the Free Software Foundation; either 10 version 2.1 of the License, or (at your option) any later version. 11 12 This library is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Lesser General Public License for more details. 16 17 You should have received a copy of the GNU Lesser General Public 18 License along with this library; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA 20 */ 21 22 #include <mrn_mysql.h> 23 24 #include "mrn_database_manager.hpp" 25 #include "mrn_encoding.hpp" 26 #include "mrn_lock.hpp" 27 #include "mrn_path_mapper.hpp" 28 29 #include <groonga/plugin.h> 30 31 // for debug 32 #define MRN_CLASS_NAME "mrn::DatabaseManager" 33 34 #ifdef WIN32 35 # include <direct.h> 36 # define MRN_MKDIR(pathname, mode) _mkdir((pathname)) 37 #else 38 # include <dirent.h> 39 # include <unistd.h> 40 # define MRN_MKDIR(pathname, mode) mkdir((pathname), (mode)) 41 #endif 42 43 extern "C" { 44 grn_rc GRN_PLUGIN_IMPL_NAME_TAGGED(init, normalizers_mysql)(grn_ctx *ctx); 45 grn_rc GRN_PLUGIN_IMPL_NAME_TAGGED(register, normalizers_mysql)(grn_ctx *ctx); 46 } 47 48 namespace mrn { DatabaseManager(grn_ctx * ctx,mysql_mutex_t * mutex)49 DatabaseManager::DatabaseManager(grn_ctx *ctx, mysql_mutex_t *mutex) 50 : ctx_(ctx), 51 cache_(NULL), 52 mutex_(mutex) { 53 } 54 ~DatabaseManager(void)55 DatabaseManager::~DatabaseManager(void) { 56 if (cache_) { 57 void *db_address; 58 GRN_HASH_EACH(ctx_, cache_, id, NULL, 0, &db_address, { 59 Database *db; 60 memcpy(&db, db_address, sizeof(grn_obj *)); 61 delete db; 62 }); 63 grn_hash_close(ctx_, cache_); 64 } 65 } 66 init(void)67 bool DatabaseManager::init(void) { 68 MRN_DBUG_ENTER_METHOD(); 69 cache_ = grn_hash_create(ctx_, 70 NULL, 71 GRN_TABLE_MAX_KEY_SIZE, 72 sizeof(grn_obj *), 73 GRN_OBJ_KEY_VAR_SIZE); 74 if (!cache_) { 75 GRN_LOG(ctx_, GRN_LOG_ERROR, 76 "failed to initialize hash table for caching opened databases"); 77 DBUG_RETURN(false); 78 } 79 80 DBUG_RETURN(true); 81 } 82 open(const char * path,Database ** db)83 int DatabaseManager::open(const char *path, Database **db) { 84 MRN_DBUG_ENTER_METHOD(); 85 86 int error = 0; 87 *db = NULL; 88 89 mrn::PathMapper mapper(path); 90 mrn::Lock lock(mutex_); 91 92 error = mrn::encoding::set(ctx_, system_charset_info); 93 if (error) { 94 DBUG_RETURN(error); 95 } 96 97 grn_id id; 98 void *db_address; 99 id = grn_hash_get(ctx_, cache_, 100 mapper.db_name(), strlen(mapper.db_name()), 101 &db_address); 102 if (id == GRN_ID_NIL) { 103 grn_obj *grn_db; 104 struct stat db_stat; 105 if (stat(mapper.db_path(), &db_stat)) { 106 GRN_LOG(ctx_, GRN_LOG_INFO, 107 "database not found. creating...: <%s>", mapper.db_path()); 108 if (path[0] == FN_CURLIB && 109 mrn_is_directory_separator(path[1])) { 110 ensure_database_directory(); 111 } 112 grn_db = grn_db_create(ctx_, mapper.db_path(), NULL); 113 if (ctx_->rc) { 114 error = ER_CANT_CREATE_TABLE; 115 my_message(error, ctx_->errbuf, MYF(0)); 116 DBUG_RETURN(error); 117 } 118 } else { 119 grn_db = grn_db_open(ctx_, mapper.db_path()); 120 if (ctx_->rc) { 121 error = ER_CANT_OPEN_FILE; 122 my_message(error, ctx_->errbuf, MYF(0)); 123 DBUG_RETURN(error); 124 } 125 } 126 *db = new Database(ctx_, grn_db); 127 grn_hash_add(ctx_, cache_, 128 mapper.db_name(), strlen(mapper.db_name()), 129 &db_address, NULL); 130 memcpy(db_address, db, sizeof(Database *)); 131 error = ensure_normalizers_registered((*db)->get()); 132 if (!error) { 133 if ((*db)->is_broken()) { 134 error = ER_CANT_OPEN_FILE; 135 char error_message[MRN_MESSAGE_BUFFER_SIZE]; 136 snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, 137 "mroonga: database: open: " 138 "The database maybe broken. " 139 "We recommend you to recreate the database. " 140 "If the database isn't broken, " 141 "you can remove this error by running " 142 "'groonga %s table_remove mroonga_operations' " 143 "on server. But the latter isn't recommended.", 144 mapper.db_path()); 145 my_message(error, error_message, MYF(0)); 146 } 147 } 148 } else { 149 memcpy(db, db_address, sizeof(Database *)); 150 grn_ctx_use(ctx_, (*db)->get()); 151 } 152 153 DBUG_RETURN(error); 154 } 155 close(const char * path)156 void DatabaseManager::close(const char *path) { 157 MRN_DBUG_ENTER_METHOD(); 158 159 mrn::PathMapper mapper(path); 160 mrn::Lock lock(mutex_); 161 162 grn_id id; 163 void *db_address; 164 id = grn_hash_get(ctx_, cache_, 165 mapper.db_name(), strlen(mapper.db_name()), 166 &db_address); 167 if (id == GRN_ID_NIL) { 168 DBUG_VOID_RETURN; 169 } 170 171 Database *db = NULL; 172 memcpy(&db, db_address, sizeof(Database *)); 173 grn_ctx_use(ctx_, db->get()); 174 if (db) { 175 delete db; 176 } 177 178 grn_hash_delete_by_id(ctx_, cache_, id, NULL); 179 180 DBUG_VOID_RETURN; 181 } 182 drop(const char * path)183 bool DatabaseManager::drop(const char *path) { 184 MRN_DBUG_ENTER_METHOD(); 185 186 mrn::PathMapper mapper(path); 187 mrn::Lock lock(mutex_); 188 189 grn_id id; 190 void *db_address; 191 id = grn_hash_get(ctx_, cache_, 192 mapper.db_name(), strlen(mapper.db_name()), 193 &db_address); 194 195 Database *db = NULL; 196 if (id == GRN_ID_NIL) { 197 struct stat dummy; 198 if (stat(mapper.db_path(), &dummy) == 0) { 199 grn_obj *grn_db = grn_db_open(ctx_, mapper.db_path()); 200 db = new Database(ctx_, grn_db); 201 } 202 } else { 203 memcpy(&db, db_address, sizeof(Database *)); 204 grn_ctx_use(ctx_, db->get()); 205 } 206 207 if (!db) { 208 DBUG_RETURN(false); 209 } 210 211 if (db->remove() == GRN_SUCCESS) { 212 if (id != GRN_ID_NIL) { 213 grn_hash_delete_by_id(ctx_, cache_, id, NULL); 214 } 215 delete db; 216 DBUG_RETURN(true); 217 } else { 218 GRN_LOG(ctx_, GRN_LOG_ERROR, 219 "failed to drop database: <%s>: <%s>", 220 mapper.db_path(), ctx_->errbuf); 221 if (id == GRN_ID_NIL) { 222 delete db; 223 } 224 DBUG_RETURN(false); 225 } 226 } 227 clear(void)228 int DatabaseManager::clear(void) { 229 MRN_DBUG_ENTER_METHOD(); 230 231 int error = 0; 232 233 mrn::Lock lock(mutex_); 234 235 grn_hash_cursor *cursor; 236 cursor = grn_hash_cursor_open(ctx_, cache_, 237 NULL, 0, NULL, 0, 238 0, -1, 0); 239 if (ctx_->rc) { 240 my_message(ER_ERROR_ON_READ, ctx_->errbuf, MYF(0)); 241 DBUG_RETURN(ER_ERROR_ON_READ); 242 } 243 244 while (grn_hash_cursor_next(ctx_, cursor) != GRN_ID_NIL) { 245 if (ctx_->rc) { 246 error = ER_ERROR_ON_READ; 247 my_message(error, ctx_->errbuf, MYF(0)); 248 break; 249 } 250 void *db_address; 251 Database *db; 252 grn_hash_cursor_get_value(ctx_, cursor, &db_address); 253 memcpy(&db, db_address, sizeof(Database *)); 254 grn_ctx_use(ctx_, db->get()); 255 grn_rc rc = grn_hash_cursor_delete(ctx_, cursor, NULL); 256 if (rc) { 257 error = ER_ERROR_ON_READ; 258 my_message(error, ctx_->errbuf, MYF(0)); 259 break; 260 } 261 delete db; 262 } 263 grn_hash_cursor_close(ctx_, cursor); 264 265 DBUG_RETURN(error); 266 } 267 error_message()268 const char *DatabaseManager::error_message() { 269 MRN_DBUG_ENTER_METHOD(); 270 DBUG_RETURN(ctx_->errbuf); 271 } 272 mkdir_p(const char * directory)273 void DatabaseManager::mkdir_p(const char *directory) { 274 MRN_DBUG_ENTER_METHOD(); 275 276 int i = 0; 277 char sub_directory[MRN_MAX_PATH_SIZE]; 278 sub_directory[0] = '\0'; 279 while (true) { 280 if (mrn_is_directory_separator(directory[i]) || 281 directory[i] == '\0') { 282 sub_directory[i] = '\0'; 283 struct stat directory_status; 284 if (stat(sub_directory, &directory_status) != 0) { 285 DBUG_PRINT("info", ("mroonga: creating directory: <%s>", sub_directory)); 286 GRN_LOG(ctx_, GRN_LOG_INFO, "creating directory: <%s>", sub_directory); 287 if (MRN_MKDIR(sub_directory, S_IRWXU) == 0) { 288 DBUG_PRINT("info", 289 ("mroonga: created directory: <%s>", sub_directory)); 290 GRN_LOG(ctx_, GRN_LOG_INFO, "created directory: <%s>", sub_directory); 291 } else { 292 DBUG_PRINT("error", 293 ("mroonga: failed to create directory: <%s>: <%s>", 294 sub_directory, strerror(errno))); 295 GRN_LOG(ctx_, GRN_LOG_ERROR, 296 "failed to create directory: <%s>: <%s>", 297 sub_directory, strerror(errno)); 298 DBUG_VOID_RETURN; 299 } 300 } 301 } 302 303 if (directory[i] == '\0') { 304 break; 305 } 306 307 sub_directory[i] = directory[i]; 308 ++i; 309 } 310 311 DBUG_VOID_RETURN; 312 } 313 ensure_database_directory(void)314 void DatabaseManager::ensure_database_directory(void) { 315 MRN_DBUG_ENTER_METHOD(); 316 317 const char *path_prefix = mrn::PathMapper::default_path_prefix; 318 if (!path_prefix) 319 DBUG_VOID_RETURN; 320 321 const char *last_path_separator; 322 last_path_separator = strrchr(path_prefix, FN_LIBCHAR); 323 #ifdef FN_LIBCHAR2 324 if (!last_path_separator) 325 last_path_separator = strrchr(path_prefix, FN_LIBCHAR2); 326 #endif 327 if (!last_path_separator) 328 DBUG_VOID_RETURN; 329 if (path_prefix == last_path_separator) 330 DBUG_VOID_RETURN; 331 332 char database_directory[MRN_MAX_PATH_SIZE]; 333 size_t database_directory_length = last_path_separator - path_prefix; 334 strncpy(database_directory, path_prefix, database_directory_length); 335 database_directory[database_directory_length] = '\0'; 336 mkdir_p(database_directory); 337 338 DBUG_VOID_RETURN; 339 } 340 ensure_normalizers_registered(grn_obj * db)341 int DatabaseManager::ensure_normalizers_registered(grn_obj *db) { 342 MRN_DBUG_ENTER_METHOD(); 343 344 int error = 0; 345 #ifdef WITH_GROONGA_NORMALIZER_MYSQL 346 { 347 # ifdef MRN_GROONGA_NORMALIZER_MYSQL_EMBEDDED 348 GRN_PLUGIN_IMPL_NAME_TAGGED(init, normalizers_mysql)(ctx_); 349 GRN_PLUGIN_IMPL_NAME_TAGGED(register, normalizers_mysql)(ctx_); 350 # else 351 grn_obj *mysql_normalizer; 352 mysql_normalizer = grn_ctx_get(ctx_, "NormalizerMySQLGeneralCI", -1); 353 if (mysql_normalizer) { 354 grn_obj_unlink(ctx_, mysql_normalizer); 355 } else { 356 grn_plugin_register(ctx_, GROONGA_NORMALIZER_MYSQL_PLUGIN_NAME); 357 } 358 # endif 359 } 360 #endif 361 362 DBUG_RETURN(error); 363 } 364 } 365